update
This commit is contained in:
parent
a266c99028
commit
219a3e9426
@ -930,4 +930,6 @@ class DealerSettingsForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = DealerSettings
|
||||
fields = "__all__"
|
||||
|
||||
|
||||
class LeadTransferForm(forms.Form):
|
||||
transfer_to = forms.ModelChoiceField(label="to",queryset=Staff.objects.all())
|
||||
@ -0,0 +1,33 @@
|
||||
# Generated by Django 4.2.17 on 2025-02-26 08:53
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('inventory', '0048_remove_dealersettings_bill_payable_account_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='lead',
|
||||
name='status',
|
||||
field=models.CharField(choices=[('new', 'New'), ('pending', 'Pending'), ('in_progress', 'In Progress'), ('qualified', 'Qualified'), ('contacted', 'Contacted'), ('converted', 'Converted'), ('canceled', 'Canceled')], db_index=True, default='new', max_length=50, verbose_name='Status'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='leadstatushistory',
|
||||
name='new_status',
|
||||
field=models.CharField(choices=[('new', 'New'), ('pending', 'Pending'), ('in_progress', 'In Progress'), ('qualified', 'Qualified'), ('contacted', 'Contacted'), ('converted', 'Converted'), ('canceled', 'Canceled')], max_length=50, verbose_name='New Status'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='leadstatushistory',
|
||||
name='old_status',
|
||||
field=models.CharField(choices=[('new', 'New'), ('pending', 'Pending'), ('in_progress', 'In Progress'), ('qualified', 'Qualified'), ('contacted', 'Contacted'), ('converted', 'Converted'), ('canceled', 'Canceled')], max_length=50, verbose_name='Old Status'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='opportunity',
|
||||
name='status',
|
||||
field=models.CharField(choices=[('new', 'New'), ('pending', 'Pending'), ('in_progress', 'In Progress'), ('qualified', 'Qualified'), ('contacted', 'Contacted'), ('converted', 'Converted'), ('canceled', 'Canceled')], default='new', max_length=20, verbose_name='Status'),
|
||||
),
|
||||
]
|
||||
@ -15,6 +15,8 @@ from django_ledger.io.io_core import get_localdate
|
||||
from django.core.exceptions import ValidationError
|
||||
from phonenumber_field.modelfields import PhoneNumberField
|
||||
from django.utils.timezone import now
|
||||
|
||||
from inventory.utils import get_user_type
|
||||
from .mixins import LocalizedNameMixin
|
||||
from django_ledger.models import EntityModel, ItemModel,EstimateModel,InvoiceModel,AccountModel
|
||||
from django.contrib.contenttypes.fields import GenericForeignKey
|
||||
@ -443,11 +445,12 @@ class Car(models.Model):
|
||||
hash_object.update(f"{self.id_car_make.name}{self.id_car_model.name}{self.year}{self.id_car_serie.name}{self.id_car_trim.name}{color}".encode('utf-8'))
|
||||
return hash_object.hexdigest()
|
||||
|
||||
def mark_as_sold(self,user):
|
||||
def mark_as_sold(self,request):
|
||||
dealer = get_user_type(request)
|
||||
self.cancel_reservation()
|
||||
self.status = CarStatusChoices.SOLD
|
||||
self.status = CarStatusChoices.SOLD
|
||||
self.save()
|
||||
Activity.objects.create(content_object=self, notes="Car Sold",created_by=user,activity_type=ActionChoices.SALE_CAR)
|
||||
Activity.objects.create(dealer=dealer,content_object=self, notes="Car Sold",created_by=request.user,activity_type=ActionChoices.SALE_CAR)
|
||||
|
||||
def cancel_reservation(self):
|
||||
if self.reservations.exists():
|
||||
@ -997,6 +1000,8 @@ class Status(models.TextChoices):
|
||||
PENDING = "pending", _("Pending")
|
||||
IN_PROGRESS = "in_progress", _("In Progress")
|
||||
QUALIFIED = "qualified", _("Qualified")
|
||||
CONTACTED = "contacted", _("Contacted")
|
||||
CONVERTED = "converted", _("Converted")
|
||||
CANCELED = "canceled", _("Canceled")
|
||||
|
||||
|
||||
@ -1245,6 +1250,7 @@ class Lead(models.Model):
|
||||
|
||||
customer.additional_info = {}
|
||||
customer.additional_info.update({"info":self.to_dict()})
|
||||
customer.additional_info.update({"stage":"qualified"})
|
||||
self.customer = customer
|
||||
self.status = Status.QUALIFIED
|
||||
customer.save()
|
||||
|
||||
@ -670,16 +670,12 @@ def create_customer_user(sender, instance, created, **kwargs):
|
||||
username=instance.email,
|
||||
email=instance.email,
|
||||
)
|
||||
instance.additional_info["customer_info"] = to_dict(instance)
|
||||
# customer_info = instance.additional_info.get("customer_info",None)
|
||||
# user.first_name = customer_info.get("first_name", None) if customer_info else ""
|
||||
# user.last_name = customer_info.get("last_name", None) if customer_info else ""
|
||||
|
||||
instance.additional_info.update({"user_info": to_dict(user)})
|
||||
user.set_unusable_password()
|
||||
user.save()
|
||||
instance.user = user
|
||||
instance.save()
|
||||
|
||||
instance.save()
|
||||
|
||||
|
||||
# Create Item
|
||||
@receiver(post_save, sender=models.Car)
|
||||
@ -894,5 +890,5 @@ def check_users_quota(sender, instance, **kwargs):
|
||||
if allowed_users is None:
|
||||
raise ValidationError(_("The user quota for staff members is not defined. Please contact support."))
|
||||
current_staff_count = instance.dealer.staff.count()
|
||||
if current_staff_count == allowed_users:
|
||||
if current_staff_count > allowed_users:
|
||||
raise ValidationError(_("You have reached the maximum number of staff users allowed for your plan."))
|
||||
|
||||
@ -121,6 +121,11 @@ urlpatterns = [
|
||||
views.schedule_lead,
|
||||
name="schedule_lead",
|
||||
),
|
||||
path(
|
||||
"crm/leads/<int:pk>/transfer/",
|
||||
views.lead_transfer,
|
||||
name="lead_transfer",
|
||||
),
|
||||
|
||||
path(
|
||||
"crm/opportunities/<int:pk>/add_note/",
|
||||
@ -364,7 +369,7 @@ urlpatterns = [
|
||||
),
|
||||
path(
|
||||
"organizations/<uuid:pk>/delete/",
|
||||
views.OrganizationDeleteView.as_view(),
|
||||
views.OrganizationDeleteView,
|
||||
name="organization_delete",
|
||||
),
|
||||
# Representative URLs
|
||||
|
||||
@ -18,7 +18,7 @@ from django.db import transaction
|
||||
from django.db.models import Func
|
||||
from django.contrib import messages
|
||||
from django.http import JsonResponse
|
||||
from django.forms import HiddenInput
|
||||
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
|
||||
@ -462,14 +462,12 @@ class AjaxHandlerView(LoginRequiredMixin, View):
|
||||
)
|
||||
|
||||
manufacturer_name, model_name, year_model = result.values()
|
||||
make = get_make(manufacturer_name)
|
||||
model = get_model(model_name, make)
|
||||
car_make = get_make(manufacturer_name)
|
||||
car_model = get_model(model_name, car_make)
|
||||
|
||||
logger.info(
|
||||
f"VIN decoded using {decoding_method}: Make={manufacturer_name}, Model={model_name}, Year={year_model}"
|
||||
)
|
||||
car_model = model
|
||||
car_make = make
|
||||
)
|
||||
|
||||
if not car_make:
|
||||
return JsonResponse(
|
||||
@ -1148,15 +1146,12 @@ class DealerDetailView(LoginRequiredMixin, DetailView):
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
|
||||
dealer = self.object
|
||||
# Fetch current staff count from the annotated queryset
|
||||
staff_count = dealer.staff_count
|
||||
|
||||
# Get the quota value dynamically
|
||||
quota_dict = get_user_quota(dealer.user)
|
||||
allowed_users = quota_dict.get("Users", None) # Fetch quota or default to None
|
||||
|
||||
context["staff_count"] = staff_count
|
||||
context["allowed_users"] = allowed_users
|
||||
context["quota_display"] = f"{staff_count}/{allowed_users}" if allowed_users is not None else "N/A"
|
||||
@ -1185,9 +1180,7 @@ class CustomerListView(LoginRequiredMixin, ListView):
|
||||
def get_queryset(self):
|
||||
query = self.request.GET.get("q")
|
||||
dealer = get_user_type(self.request)
|
||||
|
||||
customers = dealer.entity.get_customers().filter(additional_info__type="customer")
|
||||
|
||||
return apply_search_filters(customers, query)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
@ -1251,16 +1244,12 @@ def add_activity_to_customer(request, pk):
|
||||
|
||||
def CustomerCreateView(request):
|
||||
form = forms.CustomerForm()
|
||||
|
||||
if request.method == "POST":
|
||||
form = forms.CustomerForm(request.POST)
|
||||
dealer = get_user_type(request)
|
||||
|
||||
if form.is_valid():
|
||||
email = form.cleaned_data["email"]
|
||||
|
||||
# Check if customer with this email already exists
|
||||
if dealer.entity.get_customers().filter(email=email).exists():
|
||||
if form.is_valid():
|
||||
if dealer.entity.get_customers().filter(email=form.cleaned_data["email"]).exists():
|
||||
messages.error(request, _("Customer with this email already exists."))
|
||||
else:
|
||||
# Create customer name
|
||||
@ -1269,7 +1258,7 @@ def CustomerCreateView(request):
|
||||
f"{form.cleaned_data['middle_name']} "
|
||||
f"{form.cleaned_data['last_name']}"
|
||||
)
|
||||
|
||||
customer_dict = { x: request.POST[x] for x in request.POST if x != "csrfmiddlewaretoken"}
|
||||
# Create customer instance
|
||||
try:
|
||||
customer = dealer.entity.create_customer(
|
||||
@ -1278,19 +1267,12 @@ def CustomerCreateView(request):
|
||||
"customer_name": customer_name,
|
||||
"address_1": form.cleaned_data["address"],
|
||||
"phone": form.cleaned_data["phone_number"],
|
||||
"email": email,
|
||||
|
||||
"email": form.cleaned_data["email"],
|
||||
}
|
||||
)
|
||||
|
||||
customer.additional_info = {
|
||||
"customer_info": {
|
||||
"first_name": form.cleaned_data["first_name"],
|
||||
"middle_name": form.cleaned_data["middle_name"],
|
||||
"last_name": form.cleaned_data["last_name"],
|
||||
},
|
||||
"type": "customer",
|
||||
}
|
||||
customer.additional_info = {}
|
||||
customer.additional_info["customer_info"] = customer_dict
|
||||
customer.additional_info["type"] = "customer"
|
||||
customer.save()
|
||||
|
||||
messages.success(request, _("Customer created successfully."))
|
||||
@ -1328,7 +1310,16 @@ def CustomerUpdateView(request, pk):
|
||||
instance.email = customer_dict["email"]
|
||||
|
||||
customer_dict["pk"] = str(instance.pk)
|
||||
instance.additional_info["customer_info"] = customer_dict
|
||||
instance.additional_info.update({"customer_info": customer_dict})
|
||||
try:
|
||||
user = User.objects.filter(pk=int(instance.additional_info['user_info']['id'])).first()
|
||||
if user:
|
||||
user.username = customer_dict["email"]
|
||||
user.email = customer_dict["email"]
|
||||
user.save()
|
||||
except Exception as e:
|
||||
raise Exception(e)
|
||||
|
||||
instance.save()
|
||||
messages.success(request, _("Customer updated successfully."))
|
||||
return redirect("customer_list")
|
||||
@ -1653,7 +1644,9 @@ class OrganizationDetailView(DetailView):
|
||||
def OrganizationCreateView(request):
|
||||
if request.method == "POST":
|
||||
form = forms.OrganizationForm(request.POST)
|
||||
# upload logo
|
||||
if CustomerModel.objects.filter(email=request.POST["email"]).exists():
|
||||
messages.error(request, _("An organization with this email already exists."))
|
||||
return redirect("organization_create")
|
||||
|
||||
organization_dict = {
|
||||
x: request.POST[x] for x in request.POST if x != "csrfmiddlewaretoken"
|
||||
@ -1686,13 +1679,13 @@ def OrganizationCreateView(request):
|
||||
|
||||
|
||||
def OrganizationUpdateView(request,pk):
|
||||
organization = get_object_or_404(CustomerModel, pk=pk)
|
||||
organization = get_object_or_404(CustomerModel, pk=pk)
|
||||
if request.method == "POST":
|
||||
form = forms.OrganizationForm(request.POST)
|
||||
|
||||
|
||||
organization_dict = {
|
||||
x: request.POST[x] for x in request.POST if x != "csrfmiddlewaretoken"
|
||||
}
|
||||
}
|
||||
dealer = get_user_type(request)
|
||||
|
||||
instance = dealer.entity.get_customers().get(
|
||||
@ -1702,8 +1695,15 @@ def OrganizationUpdateView(request,pk):
|
||||
instance.address_1 = organization_dict["address"]
|
||||
instance.phone = organization_dict["phone_number"]
|
||||
instance.email = organization_dict["email"]
|
||||
|
||||
organization_dict["logo"] = organization.additional_info["organization_info"]["logo"]
|
||||
|
||||
image = request.FILES.get("logo")
|
||||
if image:
|
||||
file_name = default_storage.save("images/{}".format(image.name), image)
|
||||
file_url = default_storage.url(file_name)
|
||||
organization_dict["logo"] = file_url
|
||||
else:
|
||||
organization_dict["logo"] = organization.additional_info["organization_info"]["logo"]
|
||||
|
||||
organization_dict["pk"] = str(instance.pk)
|
||||
instance.additional_info["organization_info"] = organization_dict
|
||||
instance.additional_info["type"] = "organization"
|
||||
@ -1711,19 +1711,23 @@ def OrganizationUpdateView(request,pk):
|
||||
messages.success(request, _("Organization created successfully."))
|
||||
return redirect("organization_list")
|
||||
else:
|
||||
form = forms.OrganizationForm(
|
||||
form = forms.OrganizationForm(
|
||||
initial=organization.additional_info["organization_info"] or {}
|
||||
)
|
||||
form.fields.pop("logo", None)
|
||||
# form.fields.pop("logo", None)
|
||||
return render(request, "organizations/organization_form.html", {"form": form})
|
||||
|
||||
|
||||
class OrganizationDeleteView(LoginRequiredMixin, SuccessMessageMixin, DeleteView):
|
||||
model = models.Organization
|
||||
template_name = "organizations/organization_confirm_delete.html"
|
||||
success_url = reverse_lazy("organization_list")
|
||||
success_message = "Organization deleted successfully."
|
||||
|
||||
# class OrganizationDeleteView(LoginRequiredMixin, SuccessMessageMixin, DeleteView):
|
||||
# model = models.Organization
|
||||
# template_name = "organizations/organization_confirm_delete.html"
|
||||
# success_url = reverse_lazy("organization_list")
|
||||
# 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")
|
||||
|
||||
class RepresentativeListView(LoginRequiredMixin, ListView):
|
||||
model = models.Representative
|
||||
@ -2119,7 +2123,7 @@ def create_estimate(request,pk=None):
|
||||
form.initial['customer'] = customer
|
||||
|
||||
car_list = models.Car.objects.filter(dealer=dealer,colors__isnull=False,finances__isnull=False,status="available").annotate(color=F('colors__exterior__rgb'),color_name=F('colors__exterior__name')).values_list(
|
||||
'id_car_make__name', 'id_car_model__name','id_car_serie__name','id_car_trim__name','color','color_name','hash').distinct()
|
||||
'id_car_make__name', 'id_car_model__name','id_car_serie__name','id_car_trim__name','color','color_name','hash').annotate(hash_count=Count('hash')).distinct()
|
||||
context = {
|
||||
"form": form,
|
||||
"items": [
|
||||
@ -2130,7 +2134,8 @@ def create_estimate(request,pk=None):
|
||||
'trim':x[3],
|
||||
'color':x[4],
|
||||
'color_name':x[5],
|
||||
'hash': x[6]
|
||||
'hash': x[6],
|
||||
'hash_count': x[7]
|
||||
}
|
||||
for x in car_list
|
||||
],
|
||||
@ -2174,9 +2179,8 @@ def create_sale_order(request, pk):
|
||||
item.item_model.additional_info['car_info']['status'] = 'sold'
|
||||
item.item_model.save()
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
models.Car.objects.get(vin=item.item_model.name).mark_as_sold(user=request.user)
|
||||
pass
|
||||
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=pk)
|
||||
@ -2586,11 +2590,14 @@ class LeadListView(ListView):
|
||||
|
||||
def get_queryset(self):
|
||||
dealer = get_user_type(self.request)
|
||||
staff = getattr(self.request.user, "staff", None)
|
||||
|
||||
if staff:
|
||||
return models.Lead.objects.filter(dealer=dealer, staff=staff)
|
||||
return models.Lead.objects.filter(dealer=dealer)
|
||||
print(dealer.user)
|
||||
staffmember = getattr(self.request.user, "staffmember", None)
|
||||
if staffmember:
|
||||
qs = models.Lead.objects.filter(dealer=dealer)
|
||||
if staffmember.staff.staff_type == models.StaffTypes.MANAGER or self.request.user == dealer.user:
|
||||
return qs
|
||||
return qs.filter(staff=staffmember.staff)
|
||||
return models.Lead.objects.none()
|
||||
|
||||
|
||||
class LeadDetailView(DetailView):
|
||||
@ -2615,6 +2622,7 @@ class LeadDetailView(DetailView):
|
||||
context["status_history"] = models.LeadStatusHistory.objects.filter(
|
||||
lead=self.object
|
||||
)
|
||||
context["transfer_form"] = forms.LeadTransferForm()
|
||||
return context
|
||||
|
||||
|
||||
@ -2638,10 +2646,11 @@ def lead_create(request):
|
||||
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
|
||||
# staff = None
|
||||
# if hasattr(request.user, "staffmember"):
|
||||
# staff = request.user.staffmember.staff
|
||||
# instance.staff = staff
|
||||
instance.staff = form.cleaned_data.get("staff")
|
||||
|
||||
# creating customer in ledger
|
||||
customer = dealer.entity.get_customers().filter(email=instance.email).first()
|
||||
@ -2655,6 +2664,8 @@ def lead_create(request):
|
||||
"sales_tax_rate": 0.15,
|
||||
}
|
||||
)
|
||||
customer.additional_info.update({'stage': 'lead'})
|
||||
customer.save()
|
||||
instance.customer = customer
|
||||
instance.save()
|
||||
messages.success(request, "Lead created successfully!")
|
||||
@ -2769,24 +2780,27 @@ def schedule_lead(request, pk):
|
||||
instance = form.save(commit=False)
|
||||
instance.lead = lead
|
||||
instance.scheduled_by = request.user
|
||||
instance.save()
|
||||
|
||||
# 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 = StaffMember.objects.filter(user=request.user).first()
|
||||
staff_member = request.user.staffmember
|
||||
|
||||
appointment_request = AppointmentRequest.objects.create(
|
||||
date=instance.scheduled_at.date(),
|
||||
start_time=instance.scheduled_at.time(),
|
||||
end_time=(instance.scheduled_at + instance.duration).time(),
|
||||
service=service,
|
||||
staff_member=staff_member,
|
||||
)
|
||||
client = get_object_or_404(User, email=lead.email)
|
||||
try:
|
||||
appointment_request = AppointmentRequest.objects.create(
|
||||
date=instance.scheduled_at.date(),
|
||||
start_time=instance.scheduled_at.time(),
|
||||
end_time=(instance.scheduled_at + instance.duration).time(),
|
||||
service=service,
|
||||
staff_member=staff_member,
|
||||
)
|
||||
except ValidationError as e:
|
||||
messages.error(request, str(e))
|
||||
return redirect("schedule_lead", pk=lead.pk)
|
||||
|
||||
client = get_object_or_404(User, email=lead.email)
|
||||
# Create Appointment
|
||||
Appointment.objects.create(
|
||||
client=client,
|
||||
@ -2795,6 +2809,7 @@ def schedule_lead(request, pk):
|
||||
address=lead.address,
|
||||
)
|
||||
|
||||
instance.save()
|
||||
messages.success(request, "Lead scheduled and appointment created successfully!")
|
||||
return redirect("lead_list")
|
||||
else:
|
||||
@ -2805,20 +2820,33 @@ def schedule_lead(request, pk):
|
||||
return render(request, "crm/leads/schedule_lead.html", {"lead": lead, "form": form})
|
||||
|
||||
|
||||
@login_required
|
||||
def lead_transfer(request,pk):
|
||||
lead = get_object_or_404(models.Lead, pk=pk)
|
||||
if request.method == "POST":
|
||||
form = forms.LeadTransferForm(request.POST)
|
||||
if form.is_valid():
|
||||
lead.staff = form.cleaned_data["transfer_to"]
|
||||
lead.save()
|
||||
messages.success(request, "Lead transferred successfully!")
|
||||
else:
|
||||
messages.error(request, f"Invalid form data: {str(form.errors)}")
|
||||
return redirect("lead_list")
|
||||
|
||||
@login_required
|
||||
def send_lead_email(request, pk,email_pk=None):
|
||||
lead = get_object_or_404(models.Lead, pk=pk)
|
||||
status = request.GET.get("status")
|
||||
dealer = get_user_type(request)
|
||||
if status == 'draft':
|
||||
models.Email.objects.create(content_object=lead, created_by=request.user,from_email="manager@tenhal.com", to_email=request.GET.get("to"), subject=request.GET.get("subject"), message=request.GET.get("message"),status=models.EmailStatus.DRAFT)
|
||||
models.Activity.objects.create(content_object=lead, notes="Email Draft",created_by=request.user,activity_type=models.ActionChoices.EMAIL)
|
||||
models.Activity.objects.create(dealer=dealer,content_object=lead, notes="Email Draft",created_by=request.user,activity_type=models.ActionChoices.EMAIL)
|
||||
messages.success(request, _("Email Draft successfully!"))
|
||||
response = HttpResponse(redirect("lead_detail", pk=lead.pk))
|
||||
response['HX-Redirect'] = reverse('lead_detail', args=[lead.pk])
|
||||
return response
|
||||
|
||||
dealer = get_user_type(request)
|
||||
lead.status = models.Status.IN_PROGRESS
|
||||
lead.status = models.Status.CONTACTED
|
||||
lead.save()
|
||||
# lead.convert_to_customer(dealer.entity)
|
||||
|
||||
@ -3140,7 +3168,7 @@ class BillDetailView(LoginRequiredMixin, DetailView):
|
||||
|
||||
kwargs["transactions"] = transactions
|
||||
kwargs["grand_total"] = grand_total
|
||||
print(dir(txs[0]))
|
||||
|
||||
return super().get_context_data(**kwargs)
|
||||
|
||||
|
||||
|
||||
BIN
static/images/images/tenhal_hero.png
Normal file
BIN
static/images/images/tenhal_hero.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.2 MiB |
BIN
static/images/images/web-design.jpg
Normal file
BIN
static/images/images/web-design.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 452 KiB |
BIN
static/images/sold.png
Normal file
BIN
static/images/sold.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 654 KiB |
@ -105,8 +105,7 @@
|
||||
<script src="{% static 'js/projectmanagement-dashboard.js' %}"></script>
|
||||
|
||||
<script src="{% static 'vendors/mapbox-gl/mapbox-gl.js' %}"></script>
|
||||
<script src="https://unpkg.com/@turf/turf@6/turf.min.js"></script>
|
||||
<script src="https://unpkg.com/htmx.org@2.0.4"></script>
|
||||
|
||||
<script src="{% static 'vendors/swiper/swiper-bundle.min.js' %}"></script>
|
||||
<script src="{% static 'vendors/flatpickr/flatpickr.min.js' %}"></script>
|
||||
|
||||
|
||||
@ -1,6 +1,15 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load i18n static %}
|
||||
{% block content %}
|
||||
{% load crispy_forms_tags %}
|
||||
|
||||
{% block customCSS %}
|
||||
<style>
|
||||
.main-tab li:last-child {
|
||||
margin-left: auto;
|
||||
}
|
||||
</style>
|
||||
{% endblock customCSS %}
|
||||
|
||||
<div class="row g-3">
|
||||
<div class="col-12">
|
||||
@ -32,7 +41,7 @@
|
||||
<div class="col-6 col-sm-auto flex-1">
|
||||
<h3 class="fw-bolder mb-2">{{ lead.first_name }} {{ lead.last_name }}</h3>
|
||||
{% if lead.staff %}
|
||||
<p class="fs-8 mb-0 white-space-nowrap fw-bold">{{ _("Assigned to")}}: <span class="fw-light">{{ lead.staff.user.get_full_name }}</span></p>
|
||||
<p class="fs-8 mb-0 white-space-nowrap fw-bold">{{ _("Assigned to")}}: <span class="fw-light">{{ lead.staff }}</span></p>
|
||||
{% else %}
|
||||
<p class="mb-0 fw-bold">{{ _("Not Assigned")}}</p>
|
||||
{% endif %}
|
||||
@ -123,12 +132,35 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-7 col-lg-7 col-xl-8">
|
||||
<ul class="nav nav-underline fs-9 deal-details scrollbar flex-nowrap w-100 pb-1 mb-6" id="myTab" role="tablist" style="overflow-y: hidden;">
|
||||
<ul class="nav main-tab nav-underline fs-9 deal-details scrollbar flex-nowrap w-100 pb-1 mb-6 justify-content-end" id="myTab" role="tablist" style="overflow-y: hidden;">
|
||||
<li class="nav-item text-nowrap me-2" role="presentation"><a class="nav-link active" id="activity-tab" data-bs-toggle="tab" href="#tab-activity" role="tab" aria-controls="tab-activity" aria-selected="false" tabindex="-1"> <span class="fa-solid fa-chart-line me-2 tab-icon-color fs-8"></span>{{ _("Activity") }}</a></li>
|
||||
<li class="nav-item text-nowrap me-2" role="presentation"><a class="nav-link" id="notes-tab" data-bs-toggle="tab" href="#tab-notes" role="tab" aria-controls="tab-notes" aria-selected="false" tabindex="-1"> <span class="fa-solid fa-clipboard me-2 tab-icon-color fs-8"></span>{{ _("Notes") }}</a></li>
|
||||
<li class="nav-item text-nowrap me-2" role="presentation"><a class="nav-link" id="emails-tab" data-bs-toggle="tab" href="#tab-emails" role="tab" aria-controls="tab-emails" aria-selected="true"> <span class="fa-solid fa-envelope me-2 tab-icon-color fs-8"></span>{{ _("Emails") }}</a></li>
|
||||
<li class="nav-item text-nowrap ml-auto" role="presentation">
|
||||
<button class="btn btn-primary" type="button" data-bs-toggle="modal" data-bs-target="#exampleModal">Reassign Lead</button>
|
||||
<div class="modal fade" id="exampleModal" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<form class="modal-content" action="{% url 'lead_transfer' lead.pk %}" method="post">
|
||||
{% csrf_token %}
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="exampleModalLabel">Reassign Lead To Another Employee</h5>
|
||||
<button class="btn btn-close p-1" type="button" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
{{transfer_form|crispy}}
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-primary" type="submit">Save</button>
|
||||
<button class="btn btn-outline-primary" type="button" data-bs-dismiss="modal">Cancel</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="tab-content" id="myTabContent">
|
||||
<div class="tab-content" id="myTabContent">
|
||||
<div class="tab-pane fade active show" id="tab-activity" role="tabpanel" aria-labelledby="activity-tab">
|
||||
<div class="mb-1">
|
||||
<h3 class="mb-4" id="scrollspyTask">{{ _("Activities") }} <span class="fw-light fs-7">({{ activities.count}})</span></h3>
|
||||
@ -166,7 +198,7 @@
|
||||
</div>
|
||||
<p class="text-body-quaternary fs-9 mb-0 text-nowrap timeline-time"><span class="fa-regular fa-clock me-1"></span>{{ activity.created }}</p>
|
||||
</div>
|
||||
<h6 class="fs-10 fw-normal mb-3">{{ _("by") }} <a class="fw-semibold" href="#!">{{ activity.created_by.staff.user.get_full_name }}</a></h6>
|
||||
<h6 class="fs-10 fw-normal mb-3">{{ _("by") }} <a class="fw-semibold" href="#!">{{ activity.created_by }}</a></h6>
|
||||
<p class="fs-9 text-body-secondary w-sm-60 mb-5">{{ activity.notes }}</p>
|
||||
</div>
|
||||
</div>
|
||||
@ -238,7 +270,6 @@
|
||||
<ul class="nav nav-underline fs-9 flex-nowrap mb-1" id="emailTab" role="tablist">
|
||||
<li class="nav-item me-3"><a class="nav-link text-nowrap border-0 active" id="mail-tab" data-bs-toggle="tab" href="#tab-mail" aria-controls="mail-tab" role="tab" aria-selected="true">Mails ({{emails.sent.count}})<span class="text-body-tertiary fw-normal"></span></a></li>
|
||||
<li class="nav-item me-3"><a class="nav-link text-nowrap border-0" id="drafts-tab" data-bs-toggle="tab" href="#tab-drafts" aria-controls="drafts-tab" role="tab" aria-selected="true">Drafts ({{emails.draft.count}})<span class="text-body-tertiary fw-normal"></span></a></li>
|
||||
<li class="nav-item me-3"><a class="nav-link text-nowrap border-0" id="schedule-tab" data-bs-toggle="tab" href="#tab-schedule" aria-controls="schedule-tab" role="tab" aria-selected="true">Scheduled (17)</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="tab-content" id="profileTabContent">
|
||||
|
||||
@ -123,6 +123,8 @@
|
||||
<span class="badge badge-phoenix badge-phoenix-info"><span class="badge-label">{{_("In Progress")}}</span><span class="fa fa-wrench ms-1"></span></span>
|
||||
{% elif lead.status == "qualified" %}
|
||||
<span class="badge badge-phoenix badge-phoenix-success"><span class="badge-label">{{_("Qualified")}}</span><span class="fa fa-check ms-1"></span></span>
|
||||
{% elif lead.status == "contacted" %}
|
||||
<span class="badge badge-phoenix badge-phoenix-info"><span class="badge-label">{{_("Contacted")}}</span><span class="fa fa-times ms-1"></span></span>
|
||||
{% elif lead.status == "canceled" %}
|
||||
<span class="badge badge-phoenix badge-phoenix-danger"><span class="badge-label">{{_("Canceled")}}</span><span class="fa fa-times ms-1"></span></span>
|
||||
{% endif %}
|
||||
@ -136,6 +138,7 @@
|
||||
<td class="align-middle white-space-nowrap fw-semibold"><a class="text-body-highlight" href="tel:{{ lead.phone_number }}">{{ lead.phone_number }}</a></td>
|
||||
<td class="align-middle white-space-nowrap fw-semibold">
|
||||
{% if lead.get_latest_schedule %}
|
||||
{{lead.get_latest_schedule.scheduled_type}} at <br>
|
||||
<a href="{% url 'appointment:get_user_appointments' %}">
|
||||
{% if lead.get_latest_schedule.scheduled_type == "Call" %}
|
||||
<span class="badge badge-phoenix badge-phoenix-primary text-primary {% if lead.get_latest_schedule.schedule_past_date %}badge-phoenix-danger text-danger{% endif %} fw-semibold"><span class="text-primary {% if lead.get_latest_schedule.schedule_past_date %}text-danger{% endif %}" data-feather="phone"></span>
|
||||
@ -175,29 +178,31 @@
|
||||
<span class="badge badge-phoenix badge-phoenix-danger">{{ _("No") }}</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="align-middle white-space-nowrap text-end">
|
||||
<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 'lead_update' lead.id %}" class="dropdown-item text-success-dark">{% trans "Edit" %}</a>
|
||||
<a href="{% url 'send_lead_email' lead.id %}" class="dropdown-item text-success-dark">{% trans "Send Email" %}</a>
|
||||
<a href="{% url 'schedule_lead' lead.id %}" class="dropdown-item text-success-dark">{% trans "Set Schedule" %}</a>
|
||||
{% if not lead.opportunity %}
|
||||
<a href="{% url 'lead_convert' lead.id %}" class="dropdown-item text-success-dark">{% trans "Convert To Opportunity" %}</a>
|
||||
{% endif %}
|
||||
<div class="dropdown-divider"></div>
|
||||
<button class="dropdown-item text-danger" data-bs-toggle="modal" data-bs-target="#deleteModal">{% trans "Delete" %}</button>
|
||||
<td class="align-middle white-space-nowrap text-end">
|
||||
{% if user == lead.staff.user %}
|
||||
<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 'lead_update' lead.id %}" class="dropdown-item text-success-dark">{% trans "Edit" %}</a>
|
||||
<a href="{% url 'send_lead_email' lead.id %}" class="dropdown-item text-success-dark">{% trans "Send Email" %}</a>
|
||||
<a href="{% url 'schedule_lead' lead.id %}" class="dropdown-item text-success-dark">{% trans "Schedule Event" %}</a>
|
||||
{% if not lead.opportunity %}
|
||||
<a href="{% url 'lead_convert' lead.id %}" class="dropdown-item text-success-dark">{% trans "Convert" %}</a>
|
||||
{% endif %}
|
||||
<div class="dropdown-divider"></div>
|
||||
<button class="dropdown-item text-danger" data-bs-toggle="modal" data-bs-target="#deleteModal">{% trans "Delete" %}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
||||
@ -429,12 +429,8 @@
|
||||
{% else %}
|
||||
<span class="fa fa-user text-body-tertiary" style="width: 32px;"></span>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if user.dealer %}
|
||||
<h6 class="mt-2 text-body-emphasis">{{ user.dealer.get_local_name }}</h6>
|
||||
{% else %}
|
||||
<h6 class="mt-2 text-body-emphasis">{{ user.staff.get_local_name }}</h6>
|
||||
{% endif %}
|
||||
</div>
|
||||
<h6 class="mt-2 text-body-emphasis">{{ user }}</h6>
|
||||
</div>
|
||||
</div>
|
||||
<div class="overflow-auto scrollbar" style="height: 10rem;">
|
||||
|
||||
@ -3,10 +3,16 @@
|
||||
{% block title %}{{ _("Car Details") }}{% endblock %}
|
||||
{% block customCSS %}
|
||||
<style>
|
||||
.transfer{
|
||||
.disabled{
|
||||
opacity: 0.5;
|
||||
pointer-events: none;
|
||||
}
|
||||
img {
|
||||
position: absolute;
|
||||
top: 13%;
|
||||
left: 90%;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
</style>
|
||||
{% endblock customCSS %}
|
||||
|
||||
@ -42,10 +48,10 @@
|
||||
{% endif %}
|
||||
|
||||
<!-- Main row -->
|
||||
<div class="row-fluid">
|
||||
<div class="row-fluid {% if car.status == 'sold' %}disabled{% endif %}">
|
||||
<div class="row g-3 justify-content-between">
|
||||
<div class="col-lg-12 col-xl-6">
|
||||
<div class="card rounded shadow d-flex align-content-center {% if car.get_transfer %}transfer{% endif %}">
|
||||
<div class="card rounded shadow d-flex align-content-center {% if car.get_transfer %}disabled{% endif %}">
|
||||
<p class="card-header rounded-top fw-bold">{% trans 'Car Details' %}</p>
|
||||
<div class="card-body">
|
||||
<div class="table-responsive scrollbar mb-3">
|
||||
@ -389,6 +395,9 @@
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% if car.status == 'sold' %}
|
||||
<img src="{% static 'images/sold.png' %}" width="200" height="100" alt="">
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<!-- Custom Card Modal -->
|
||||
@ -450,8 +459,7 @@
|
||||
<i class="fas fa-check"></i> {% trans 'Yes' %}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@ -470,7 +478,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
|
||||
@ -587,6 +594,15 @@
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
if('{{car.status}}' == "sold"){
|
||||
document.querySelector(".row-fluid").querySelectorAll("button").forEach((button) => {
|
||||
button.classList.add("d-none");
|
||||
});
|
||||
document.querySelector(".row-fluid").querySelectorAll("a").forEach((button) => {
|
||||
button.classList.add("d-none");
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
@ -110,6 +110,7 @@
|
||||
<th class="align-middle" scope="col">{{ _("Model") }}</th>
|
||||
<th class="align-middle" scope="col">{{ _("Year") }}</th>
|
||||
<th class="align-middle" scope="col">{{ _("Trim") }}</th>
|
||||
<th class="align-middle" scope="col">{{ _("Color") }}</th>
|
||||
<th class="align-middle" scope="col">{{ _("Age") }}</th>
|
||||
<th class="align-middle" scope="col">{{ _("Status") }}</th>
|
||||
<th class="align-middle" scope="col"></th>
|
||||
@ -133,6 +134,16 @@
|
||||
<td class="align-middle white-space-nowrap">
|
||||
<p class="fw-bold text-body mb-0">{{car.id_car_trim }}</p>
|
||||
</td>
|
||||
<td class="align-middle white-space-nowrap">
|
||||
<div class="d-flex flex-row align-items-center">
|
||||
<div class="d-flex flex-column align-items-center">
|
||||
<span class="color-div" style="background: linear-gradient(90deg, rgba({{ car.colors.exterior.rgb }},1) 10%, rgba({{ car.colors.exterior.rgb }},0.10) 100%);" title="{{ car.colors.exterior.get_local_name }}"></span><span>{{ car.colors.exterior.get_local_name }}</span>
|
||||
</div>
|
||||
<div class="d-flex flex-column align-items-center">
|
||||
<span class="color-div" style="background: linear-gradient(90deg, rgba({{ car.colors.interior.rgb }},1) 10%, rgba({{ car.colors.interior.rgb }},0.10) 100%);" title="{{ car.colors.interior.get_local_name }}"></span><span>{{ car.colors.interior.get_local_name }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<td class="align-middle white-space-nowrap">
|
||||
<p class="fw-bold text-body mb-0">{{car.receiving_date|timesince}}</p>
|
||||
|
||||
@ -111,12 +111,13 @@
|
||||
<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.info.crn }}</td>
|
||||
<td class="phone align-middle white-space-nowrap fw-semibold ps-4 border-end border-translucent">{{ org.additional_info.info.vrn }}</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>
|
||||
|
||||
@ -58,7 +58,22 @@
|
||||
<section class="pt-5 pb-9 bg-body-emphasis dark__bg-gray-1200 border-top">
|
||||
<div class="row-small mt-3">
|
||||
<div class="d-flex justify-content-between align-items-end mb-4 mx-3">
|
||||
<h2 class="mb-0"><i class="fa-regular fa-file-lines"></i> {% trans 'Quotation' %}</h2>
|
||||
<div class="d-flex flex-row align-items-center gap-2">
|
||||
<h2 class="mb-0"><i class="fa-regular fa-file-lines"></i> {% trans 'Quotation' %}</h2>
|
||||
<div class="fs-9 text-body-secondary fw-semibold mb-0">
|
||||
{% if estimate.status == 'draft' %}
|
||||
<span class="badge text-bg-warning">{% trans "Draft" %}</span>
|
||||
{% elif estimate.status == 'in_review' %}
|
||||
<span class="badge text-bg-info">{% trans "In Review" %}</span>
|
||||
{% elif estimate.status == 'approved' %}
|
||||
<span class="badge text-bg-success">{% trans "Approved" %}</span>
|
||||
{% elif estimate.status == 'completed' %}
|
||||
<span class="badge text-bg-success">{% trans "completed" %}</span>
|
||||
{% elif estimate.status == 'canceled' %}
|
||||
<span class="badge text-bg-danger">{% trans "canceled" %}</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
{% if estimate.invoicemodel_set.first %}
|
||||
<a href="{% url 'invoice_detail' estimate.invoicemodel_set.first.pk %}" class="btn btn-primary btn-lg me-1 mb-1" type="button"><i class="fa-solid fa-receipt"></i> View Invoice</a>
|
||||
@ -190,7 +205,7 @@
|
||||
<td class="align-middle ps-4 fw-semibold text-body-highlight" colspan="7">{% trans "Additional Services" %}</td>
|
||||
<td class="align-middle text-start fw-semibold">
|
||||
{% for service in data.additionals %}
|
||||
<small><span class="fw-semibold">+ {{service.name}} - {{service.total}}</span></small><br>
|
||||
<small><span class="fw-semibold">+ {{service.name}} - {{service.price_}}</span></small><br>
|
||||
{% endfor %}
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@ -18,7 +18,7 @@
|
||||
<div class="mb-2 col-sm-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}}</option>
|
||||
<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>
|
||||
{% empty %}
|
||||
<option disabled>{% trans "No Cars Found" %}</option>
|
||||
{% endfor %}
|
||||
|
||||
@ -43,16 +43,12 @@
|
||||
</td>
|
||||
<td class="align-middle product white-space-nowrap">{{ estimate.get_status_action_date }}</td>
|
||||
<td class="align-middle product white-space-nowrap">{{ estimate.created }}</td>
|
||||
<td class="text-center">
|
||||
<td class="align-middle product white-space-nowrap">
|
||||
<a href="{% url 'estimate_detail' estimate.pk %}"
|
||||
class="btn btn-sm btn-phoenix-success">
|
||||
<i class="fa-regular fa-eye"></i>
|
||||
{% trans "view"|capfirst %}
|
||||
</a>
|
||||
<a href="{% url 'estimate_detail' estimate.pk %}"
|
||||
class="btn btn-sm btn-phoenix-success">
|
||||
{% trans "pdf"|capfirst %}
|
||||
</a>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
|
||||
@ -2,7 +2,14 @@
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}{{ _("View Estimate") }}{% endblock title %}
|
||||
|
||||
{% block customCSS %}
|
||||
<style>
|
||||
.disabled{
|
||||
opacity: 0.5;
|
||||
pointer-events: none;
|
||||
}
|
||||
</style>
|
||||
{% endblock customCSS %}
|
||||
{% block content %}
|
||||
<div class="modal fade" id="confirmModal" tabindex="-1" aria-labelledby="confirmModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-sm">
|
||||
@ -55,9 +62,24 @@
|
||||
<!-- ============================================-->
|
||||
<!-- <section> begin ============================-->
|
||||
<section class="pt-5 pb-9 bg-body-emphasis dark__bg-gray-1200 border-top">
|
||||
<div class="row-small mt-3 mx-3">
|
||||
<div class="row-small mt-3 mx-3">
|
||||
<div class="d-flex justify-content-between align-items-end mb-4 mx-3">
|
||||
<h2 class="mb-0"><i class="fa-solid fa-receipt"></i> {% trans 'Invoice' %}</h2>
|
||||
<div class="d-flex flex-row align-items-center gap-2">
|
||||
<h2 class="mb-0"><i class="fa-solid fa-receipt"></i> {% trans 'Invoice' %}</h2>
|
||||
<div class="fs-9 text-body-secondary fw-semibold mt-2">
|
||||
{% if invoice.invoice_status == 'draft' %}
|
||||
<span class="badge text-bg-warning">{% trans "Draft" %}</span>
|
||||
{% elif invoice.invoice_status == 'in_review' %}
|
||||
<span class="badge text-bg-info">{% trans "In Review" %}</span>
|
||||
{% elif invoice.invoice_status == 'approved' %}
|
||||
<span class="badge text-bg-info">{% trans "Approved" %}</span>
|
||||
{% elif invoice.invoice_status == 'declined' %}
|
||||
<span class="badge text-bg-danger">{% trans "Declined" %}</span>
|
||||
{% elif invoice.invoice_status == 'paid' %}
|
||||
<span class="badge text-bg-success">{% trans "Paid" %}</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
{% if invoice.invoice_status == 'in_review' %}
|
||||
<button id="accept_invoice" class="btn btn-phoenix-secondary" data-bs-toggle="modal" data-bs-target="#confirmModal"><span class="d-none d-sm-inline-block"><i class="fa-solid fa-check-double"></i> {% trans 'Accept' %}</span></button>
|
||||
@ -66,14 +88,14 @@
|
||||
<a href="{% url 'payment_create' invoice.pk %}" class="btn btn-phoenix-success"><span class="d-none d-sm-inline-block"><i class="fa-solid fa-money-bill"></i> {% trans 'Record Payment' %}</span></a>
|
||||
{% endif %}
|
||||
{% if not invoice.is_paid %}
|
||||
<button id="mark_invoice_as_paid" class="btn btn-phoenix-secondary" data-bs-toggle="modal" data-bs-target="#mark_as_paid_Modal"><span class="d-none d-sm-inline-block"><i class="fa-solid fa-money-check-dollar"></i> {% trans 'Mark as Paid' %}</span></button>
|
||||
<button {% if invoice.is_review %}disabled{% endif %} id="mark_invoice_as_paid" class="btn btn-phoenix-secondary" data-bs-toggle="modal" data-bs-target="#mark_as_paid_Modal"><span class="d-none d-sm-inline-block"><i class="fa-solid fa-money-check-dollar"></i> {% trans 'Mark as Paid' %}</span></button>
|
||||
{% endif %}
|
||||
<a href="{% url 'invoice_preview' invoice.pk %}" class="btn btn-phoenix-primary"><span class="d-none d-sm-inline-block"><i class="fa-regular fa-eye"></i> {% trans 'Preview' %}</span></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ============================================-->
|
||||
<div class="card mb-5">
|
||||
<div class="card mb-5 {% if invoice.is_review %}disabled{% endif %}">
|
||||
<div class="card-body">
|
||||
<div class="row g-4 g-xl-1 g-xxl-3 justify-content-between">
|
||||
<div class="col-sm-auto">
|
||||
@ -145,7 +167,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<!-- <section> begin ============================-->
|
||||
<div class="bg-body dark__bg-gray-1100 p-4 mb-4 rounded-2 text-body-tertiary">
|
||||
<div class="bg-body dark__bg-gray-1100 p-4 mb-4 rounded-2 text-body-tertiary {% if invoice.is_review %}disabled{% endif %}">
|
||||
<div class="row g-4">
|
||||
<div class="col-12 col-lg-3">
|
||||
<div class="row g-4 g-lg-2">
|
||||
@ -205,7 +227,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="px-0">
|
||||
<div class="px-0 {% if invoice.is_review %}disabled{% endif %}">
|
||||
<div class="table-responsive scrollbar">
|
||||
<table id="invoice-table" class="table fs-9 text-body mb-0">
|
||||
<thead class="bg-body-secondary">
|
||||
@ -249,7 +271,7 @@
|
||||
<td class="align-middle ps-4 fw-semibold text-body-highlight" colspan="7">{% trans "Additional Services" %}</td>
|
||||
<td class="align-middle text-start fw-bold">
|
||||
{% for service in data.additionals %}
|
||||
<small><span class="fw-bold">+ {{service.name}} - {{service.price}}</span></small><br>
|
||||
<small><span class="fw-bold">+ {{service.name}} - {{service.price_}}</span></small><br>
|
||||
{% endfor %}
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user