Merge branch 'main' of http://10.10.1.120:3000/tenhal_admin/haikal
1
.idea/car_inventory.iml
generated
@ -33,6 +33,7 @@
|
||||
<orderEntry type="library" name="fullcalendar" level="application" />
|
||||
<orderEntry type="library" name="popper.js" level="application" />
|
||||
<orderEntry type="library" name="tempusdominus-bootstrap-4" level="application" />
|
||||
<orderEntry type="library" name="chart.js" level="application" />
|
||||
</component>
|
||||
<component name="TemplatesService">
|
||||
<option name="TEMPLATE_CONFIGURATION" value="Django" />
|
||||
|
||||
2
.idea/jsLibraryMappings.xml
generated
@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="JavaScriptLibraryMappings">
|
||||
<file url="file://$PROJECT_DIR$" libraries="{@turf/turf, @zxing, apexcharts, bootstrap-icons, fullcalendar, jquery, jquery-3.5.1, line, moment-timezone, moment.js, popper.js, quagga, sweetalert2, tempusdominus-bootstrap-4, tesseract.js}" />
|
||||
<file url="file://$PROJECT_DIR$" libraries="{@turf/turf, @zxing, apexcharts, bootstrap-icons, chart.js, fullcalendar, jquery, jquery-3.5.1, line, moment-timezone, moment.js, popper.js, quagga, sweetalert2, tempusdominus-bootstrap-4, tesseract.js}" />
|
||||
</component>
|
||||
</project>
|
||||
@ -1,6 +1,7 @@
|
||||
from django.contrib import admin
|
||||
from . import models
|
||||
from django_ledger import models as ledger_models
|
||||
from django_pdf_actions.actions import export_to_pdf_landscape, export_to_pdf_portrait
|
||||
|
||||
|
||||
admin.site.register(models.Dealer)
|
||||
@ -9,7 +10,7 @@ admin.site.register(models.Vendor)
|
||||
# admin.site.register(models.SaleQuotation)
|
||||
# admin.site.register(models.SaleQuotationCar)
|
||||
# admin.site.register(models.SalesOrder)
|
||||
admin.site.register(models.Car)
|
||||
|
||||
admin.site.register(models.CarFinance)
|
||||
admin.site.register(models.CarColors)
|
||||
admin.site.register(models.CarRegistration)
|
||||
@ -38,11 +39,18 @@ admin.site.register(models.Activity)
|
||||
admin.site.register(models.Schedule)
|
||||
admin.site.register(models.Notes)
|
||||
|
||||
|
||||
@admin.register(models.Car)
|
||||
class CarAdmin(admin.ModelAdmin):
|
||||
search_fields = ('vin',)
|
||||
actions = [export_to_pdf_landscape, export_to_pdf_portrait]
|
||||
|
||||
@admin.register(models.CarMake)
|
||||
class CarMakeAdmin(admin.ModelAdmin):
|
||||
list_display = ('name', 'arabic_name', 'is_sa_import')
|
||||
search_fields = ('name', 'arabic_name')
|
||||
list_filter = ('is_sa_import', 'name',)
|
||||
actions = [export_to_pdf_landscape, export_to_pdf_portrait]
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Car Make"
|
||||
@ -52,7 +60,7 @@ class CarMakeAdmin(admin.ModelAdmin):
|
||||
@admin.register(models.CarModel)
|
||||
class CarModelAdmin(admin.ModelAdmin):
|
||||
list_display = ('name', 'arabic_name', 'id_car_make', 'get_is_sa_import')
|
||||
search_fields = ('name', 'arabic_name')
|
||||
search_fields = ('id_car_model', 'name', 'arabic_name')
|
||||
list_filter = ('id_car_make__is_sa_import', 'id_car_make')
|
||||
sortable_by = ['name', 'arabic_name', 'id_car_make']
|
||||
|
||||
@ -69,7 +77,7 @@ class CarModelAdmin(admin.ModelAdmin):
|
||||
@admin.register(models.CarSerie)
|
||||
class CarSeriesAdmin(admin.ModelAdmin):
|
||||
list_display = ('name', 'arabic_name', 'id_car_model', )
|
||||
search_fields = ('name', 'id_car_model__name')
|
||||
search_fields = ('id_car_serie', 'name', 'id_car_model__name', 'id_car_model__id_car_make__name')
|
||||
list_filter = ('id_car_model__id_car_make__is_sa_import',
|
||||
'id_car_model__id_car_make__name',)
|
||||
|
||||
@ -115,3 +123,7 @@ class CarOptionAdmin(admin.ModelAdmin):
|
||||
# search_fields = ('user__username', 'action')
|
||||
# list_filter = ('timestamp',)
|
||||
|
||||
@admin.register(ledger_models.ItemTransactionModel)
|
||||
class ItemTransactionModelAdmin(admin.ModelAdmin):
|
||||
|
||||
actions = [export_to_pdf_landscape, export_to_pdf_portrait]
|
||||
@ -94,9 +94,9 @@ urlpatterns = [
|
||||
),
|
||||
path("crm/leads/<int:pk>/delete/", views.LeadDeleteView, name="lead_delete"),
|
||||
path("crm/leads/<int:pk>/lead-convert/", views.lead_convert, name="lead_convert"),
|
||||
path("crm/leads/<int:pk>/add-note/", views.add_note_to_lead, name="add_note"),
|
||||
path('crm/leads/<int:pk>/update-note/', views.update_note, name='update_note'),
|
||||
path("crm/leads/<int:pk>/delete-note/", views.delete_note, name="delete_note"),
|
||||
path("crm/leads/<int:pk>/add-note/", views.add_note_to_lead, name="add_note_to_lead"),
|
||||
path('crm/leads/<int:pk>/update-note/', views.update_note, name='update_note_to_lead'),
|
||||
path("crm/leads/<int:pk>/delete-note/", views.delete_note, name="delete_note_to_lead"),
|
||||
path(
|
||||
"crm/leads/<int:pk>/add-activity/",
|
||||
views.add_activity_to_lead,
|
||||
|
||||
@ -35,8 +35,8 @@ def get_jwt_token():
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
data = {
|
||||
"api_token": "f5204a00-6f31-4de2-96d8-ed998e0d230c",
|
||||
"api_secret": "8c11320781a5b8f4f327b6937e6f8241",
|
||||
"api_token": settings.CAR_API_TOKEN,
|
||||
"api_secret": settings.CAR_API_SECRET,
|
||||
}
|
||||
try:
|
||||
response = requests.post(url, headers=headers, json=data)
|
||||
@ -247,6 +247,7 @@ def get_financial_values(model):
|
||||
additional_services = []
|
||||
|
||||
for i in data:
|
||||
print(i)
|
||||
if i.item_model.additional_info["additional_services"]:
|
||||
additional_services.extend(
|
||||
[
|
||||
|
||||
@ -3,6 +3,9 @@ from appointment.models import Appointment,AppointmentRequest,Service,StaffMembe
|
||||
from datetime import timedelta
|
||||
from calendar import month_name
|
||||
from random import randint
|
||||
|
||||
from django_pdf_actions.actions import export_to_pdf_landscape
|
||||
from reportlab.lib.pagesizes import landscape, A4
|
||||
from rich import print
|
||||
from decimal import Decimal
|
||||
from django.core.paginator import Paginator
|
||||
@ -139,6 +142,7 @@ from django_ledger.views.mixins import (
|
||||
BaseDateNavigationUrlMixIn,
|
||||
PDFReportMixIn,
|
||||
)
|
||||
from django_pdf_actions import actions
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@ -265,6 +269,7 @@ class HomeView(TemplateView):
|
||||
template_name = "index.html"
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
# Redirect unauthenticated users to the welcome page
|
||||
if not request.user.is_authenticated:
|
||||
return redirect("welcome")
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
@ -273,33 +278,55 @@ class HomeView(TemplateView):
|
||||
context = super().get_context_data(**kwargs)
|
||||
dealer = get_user_type(self.request)
|
||||
|
||||
total_cars = models.Car.objects.filter(dealer=dealer).count()
|
||||
total_reservations = models.CarReservation.objects.filter(
|
||||
reserved_until__gte=timezone.now()
|
||||
).count()
|
||||
cars_in_house = models.CarLocation.objects.filter(
|
||||
owner=dealer,
|
||||
).count()
|
||||
cars_outside = total_cars - cars_in_house
|
||||
stats = models.CarFinance.objects.aggregate(
|
||||
total_cost_price=Sum("cost_price"),
|
||||
total_selling_price=Sum("selling_price"),
|
||||
)
|
||||
total_cost_price = stats["total_cost_price"] or 0
|
||||
total_selling_price = stats["total_selling_price"] or 0
|
||||
total_profit = total_selling_price - total_cost_price
|
||||
try:
|
||||
# Fetch car-related statistics
|
||||
total_cars = models.Car.objects.filter(dealer=dealer).count()
|
||||
total_reservations = models.CarReservation.objects.filter(
|
||||
reserved_until__gte=timezone.now()
|
||||
).count()
|
||||
cars_in_house = models.CarLocation.objects.filter(
|
||||
owner=dealer,
|
||||
).count()
|
||||
cars_outside = total_cars - cars_in_house
|
||||
|
||||
# Fetch financial statistics
|
||||
stats = models.CarFinance.objects.aggregate(
|
||||
total_cost_price=Sum("cost_price"),
|
||||
total_selling_price=Sum("selling_price"),
|
||||
)
|
||||
total_cost_price = stats.get("total_cost_price", 0) or 0
|
||||
total_selling_price = stats.get("total_selling_price", 0) or 0
|
||||
total_profit = total_selling_price - total_cost_price
|
||||
|
||||
# Prepare context data
|
||||
context.update({
|
||||
"dealer": dealer,
|
||||
"total_cars": total_cars,
|
||||
"cars_in_house": cars_in_house,
|
||||
"cars_outside": cars_outside,
|
||||
"total_reservations": total_reservations,
|
||||
"total_cost_price": total_cost_price,
|
||||
"total_selling_price": total_selling_price,
|
||||
"total_profit": total_profit,
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
# Log the error (you can use Django's logging framework)
|
||||
print(f"Error fetching data: {e}")
|
||||
# Provide default values in case of an error
|
||||
context.update({
|
||||
"dealer": dealer,
|
||||
"total_cars": 0,
|
||||
"cars_in_house": 0,
|
||||
"cars_outside": 0,
|
||||
"total_reservations": 0,
|
||||
"total_cost_price": 0,
|
||||
"total_selling_price": 0,
|
||||
"total_profit": 0,
|
||||
})
|
||||
|
||||
context["dealer"] = dealer
|
||||
context["total_cars"] = total_cars
|
||||
context["cars_in_house"] = cars_in_house
|
||||
context["cars_outside"] = cars_outside
|
||||
context["total_reservations"] = total_reservations
|
||||
context["total_cost_price"] = total_cost_price
|
||||
context["total_selling_price"] = total_selling_price
|
||||
context["total_profit"] = total_profit
|
||||
return context
|
||||
|
||||
|
||||
class TestView(TemplateView):
|
||||
template_name = "test.html"
|
||||
|
||||
@ -617,9 +644,7 @@ class CarInventory(LoginRequiredMixin, ListView):
|
||||
id_car_trim=trim_id,
|
||||
).order_by("receiving_date")
|
||||
|
||||
if query:
|
||||
cars = cars.filter(Q(vin__icontains=query))
|
||||
return cars
|
||||
return apply_search_filters(cars, query)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
@ -652,11 +677,12 @@ class CarListView(LoginRequiredMixin, ListView):
|
||||
model = models.Car
|
||||
template_name = "inventory/car_list_view.html"
|
||||
context_object_name = "cars"
|
||||
paginate_by = 10
|
||||
paginate_by = 20
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
dealer = get_user_type(self.request)
|
||||
cars = models.Car.objects.filter(dealer=dealer)
|
||||
dealer = get_user_type(self.request)
|
||||
cars = models.Car.objects.filter(dealer=dealer).order_by("receiving_date")
|
||||
|
||||
context["stats"] = {
|
||||
'all': cars.count(),
|
||||
'available':cars.filter(status='available').count(),
|
||||
@ -664,16 +690,15 @@ class CarListView(LoginRequiredMixin, ListView):
|
||||
'sold':cars.filter(status='sold').count(),
|
||||
'transfer':cars.filter(status='transfer').count()
|
||||
}
|
||||
context['make'] = models.CarMake.objects.all()
|
||||
context['make'] = models.CarMake.objects.filter(car__in=cars).distinct()
|
||||
context['model'] = models.CarModel.objects.none()
|
||||
context['year'] = models.Car.objects.none()
|
||||
|
||||
make = self.request.GET.get('make')
|
||||
model = self.request.GET.get('model')
|
||||
|
||||
|
||||
if make:
|
||||
make_ = models.CarMake.objects.get(pk=int(make))
|
||||
context['model'] = make_.carmodel_set.all()
|
||||
make_ = models.CarMake.objects.get(id_car_make=int(make))
|
||||
context['model'] = make_.carmodel_set.filter(car__in=cars).distinct()
|
||||
if make and model:
|
||||
make_ = models.CarMake.objects.get(id_car_make=int(make))
|
||||
model_ = models.CarModel.objects.get(id_car_model=int(model))
|
||||
@ -1198,17 +1223,9 @@ class CustomerListView(LoginRequiredMixin, ListView):
|
||||
query = self.request.GET.get("q")
|
||||
dealer = get_user_type(self.request)
|
||||
|
||||
customers = dealer.entity.get_customers().filter(
|
||||
active=True, additional_info__type="customer"
|
||||
)
|
||||
customers = dealer.entity.get_customers().filter(additional_info__type="customer")
|
||||
|
||||
if query:
|
||||
customers = customers.filter(
|
||||
Q(first_name__icontains=query)
|
||||
| Q(last_name__icontains=query)
|
||||
| Q(additional_info__info__icontains=query)
|
||||
)
|
||||
return customers
|
||||
return apply_search_filters(customers, query)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
@ -1255,7 +1272,7 @@ def add_note_to_customer(request, pk):
|
||||
return redirect("customer_detail", pk=pk)
|
||||
else:
|
||||
form = forms.NoteForm()
|
||||
return render(request, "crm/add_note.html", {"form": form, "customer": customer})
|
||||
return render(request, "crm/note_form.html", {"form": form, "customer": customer})
|
||||
|
||||
|
||||
def add_activity_to_customer(request, pk):
|
||||
@ -1305,6 +1322,7 @@ def CustomerCreateView(request):
|
||||
"address_1": form.cleaned_data["address"],
|
||||
"phone": form.cleaned_data["phone_number"],
|
||||
"email": email,
|
||||
|
||||
}
|
||||
)
|
||||
|
||||
@ -1388,10 +1406,11 @@ class VendorListView(LoginRequiredMixin, ListView):
|
||||
ordering = ["-created"]
|
||||
|
||||
def get_queryset(self):
|
||||
query = self.request.GET.get("q")
|
||||
dealer = get_user_type(self.request)
|
||||
# vendors = models.Vendor.objects.filter(dealer=dealer)
|
||||
return dealer.entity.get_vendors().filter(active=True)
|
||||
# return vendors
|
||||
vendors = dealer.entity.get_vendors().filter(active=True)
|
||||
return apply_search_filters(vendors, query)
|
||||
|
||||
|
||||
|
||||
# class VendorDetailView(LoginRequiredMixin, DetailView):
|
||||
@ -1828,8 +1847,10 @@ class UserListView(LoginRequiredMixin, ListView):
|
||||
template_name = "users/user_list.html"
|
||||
|
||||
def get_queryset(self):
|
||||
query = self.request.GET.get("q")
|
||||
dealer = get_user_type(self.request)
|
||||
return models.Staff.objects.filter(dealer=dealer).all()
|
||||
staff = models.Staff.objects.filter(dealer=dealer).all()
|
||||
return apply_search_filters(staff, query)
|
||||
|
||||
|
||||
class UserDetailView(LoginRequiredMixin, DetailView):
|
||||
@ -1911,12 +1932,13 @@ class OrganizationListView(LoginRequiredMixin, ListView):
|
||||
paginate_by = 10
|
||||
|
||||
def get_queryset(self):
|
||||
query = self.request.GET.get("q")
|
||||
dealer = get_user_type(self.request)
|
||||
return (
|
||||
dealer.entity.get_customers()
|
||||
.filter(additional_info__type="organization", active=True)
|
||||
.all()
|
||||
)
|
||||
organization = dealer.entity.get_customers().filter(additional_info__type="organization", active=True)
|
||||
|
||||
return apply_search_filters(organization, query)
|
||||
|
||||
|
||||
|
||||
|
||||
class OrganizationDetailView(DetailView):
|
||||
@ -2007,8 +2029,10 @@ class RepresentativeListView(LoginRequiredMixin, ListView):
|
||||
paginate_by = 10
|
||||
|
||||
def get_queryset(self):
|
||||
query = self.request.GET.get("q")
|
||||
dealer = get_user_type(self.request)
|
||||
return models.Representative.objects.filter(dealer=dealer).all()
|
||||
representative = models.Representative.objects.filter(dealer=dealer)
|
||||
return apply_search_filters(representative, query)
|
||||
|
||||
|
||||
class RepresentativeDetailView(DetailView):
|
||||
@ -2233,8 +2257,10 @@ class BankAccountListView(LoginRequiredMixin, ListView):
|
||||
paginate_by = 10
|
||||
|
||||
def get_queryset(self):
|
||||
query = self.request.GET.get("q")
|
||||
dealer = get_user_type(self.request)
|
||||
return BankAccountModel.objects.filter(entity_model=dealer.entity)
|
||||
bank_accounts = BankAccountModel.objects.filter(entity_model=dealer.entity)
|
||||
return apply_search_filters(bank_accounts, query)
|
||||
|
||||
|
||||
class BankAccountCreateView(LoginRequiredMixin, SuccessMessageMixin, CreateView):
|
||||
@ -2304,9 +2330,10 @@ class AccountListView(LoginRequiredMixin, ListView):
|
||||
paginate_by = 10
|
||||
|
||||
def get_queryset(self):
|
||||
query = self.request.GET.get("q")
|
||||
dealer = get_user_type(self.request)
|
||||
entity = dealer.entity
|
||||
return entity.get_all_accounts()
|
||||
accounts = dealer.entity.get_all_accounts()
|
||||
return apply_search_filters(accounts, query)
|
||||
|
||||
|
||||
class AccountCreateView(LoginRequiredMixin, SuccessMessageMixin, CreateView):
|
||||
@ -2400,17 +2427,24 @@ def account_delete(request, pk):
|
||||
|
||||
# Sales list
|
||||
def sales_list_view(request):
|
||||
transactions = ItemTransactionModel.objects.all()[:10]
|
||||
txs = get_item_transactions(transactions)
|
||||
context = {"txs":txs}
|
||||
return render(request, "sales/sales_list.html",context)
|
||||
dealer = get_user_type(request)
|
||||
entity = dealer.entity
|
||||
|
||||
transactions = ItemTransactionModel.objects.for_entity(entity_slug=entity.slug, user_model=dealer.user)
|
||||
paginator = Paginator(transactions, 10)
|
||||
page_number = request.GET.get('page')
|
||||
page_obj = paginator.get_page(page_number)
|
||||
txs = get_item_transactions(page_obj)
|
||||
context = {"txs": txs, "page_obj": page_obj}
|
||||
return render(request, "sales/sales_list.html", context)
|
||||
|
||||
|
||||
# Estimates
|
||||
class EstimateListView(LoginRequiredMixin, ListView):
|
||||
model = EstimateModel
|
||||
template_name = "sales/estimates/estimate_list.html"
|
||||
context_object_name = "estimates"
|
||||
paginate_by = 10
|
||||
paginate_by = 20
|
||||
|
||||
def get_queryset(self):
|
||||
dealer = get_user_type(self.request)
|
||||
@ -2422,6 +2456,10 @@ class EstimateListView(LoginRequiredMixin, ListView):
|
||||
return queryset
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# class EstimateCreateView(LoginRequiredMixin, SuccessMessageMixin, CreateView):
|
||||
# model = EstimateModel
|
||||
# form_class = EstimateModelCreateForm
|
||||
@ -2766,9 +2804,10 @@ class InvoiceListView(LoginRequiredMixin, ListView):
|
||||
paginate_by = 20
|
||||
|
||||
def get_queryset(self):
|
||||
query = self.request.GET.get("q")
|
||||
dealer = get_user_type(self.request)
|
||||
entity = dealer.entity
|
||||
return entity.get_invoices()
|
||||
invoices = dealer.entity.get_invoices()
|
||||
return apply_search_filters(invoices, query)
|
||||
|
||||
|
||||
class InvoiceDetailView(LoginRequiredMixin, DetailView):
|
||||
@ -3106,7 +3145,11 @@ class LeadListView(ListView):
|
||||
|
||||
def get_queryset(self):
|
||||
dealer = get_user_type(self.request)
|
||||
return models.Lead.objects.filter(dealer=dealer).all()
|
||||
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)
|
||||
|
||||
|
||||
class LeadDetailView(DetailView):
|
||||
@ -4314,9 +4357,25 @@ class EmployeeCalendarView(ListView):
|
||||
model = Appointment
|
||||
context_object_name = 'appointments'
|
||||
|
||||
# def get_context_data(self):
|
||||
# self.context['dealer'] = get_user_type(self.request)
|
||||
# dealer =
|
||||
# return Appointment.objects.all()
|
||||
def get_queryset(self):
|
||||
query = self.request.GET.get('q')
|
||||
dealer = get_user_type(self.request)
|
||||
staff = getattr(self.request, 'staff', None)
|
||||
if staff:
|
||||
appointments = Appointment.objects.filter(appointment_request__staff_member=staff, ppointment_request__date__gt=timezone.now())
|
||||
appointments = Appointment.objects.filter(appointment_request__date__gt=timezone.now())
|
||||
return apply_search_filters(appointments, query)
|
||||
|
||||
|
||||
|
||||
def apply_search_filters(queryset, query):
|
||||
if not query:
|
||||
return queryset
|
||||
|
||||
search_filters = Q()
|
||||
model = queryset.model
|
||||
|
||||
for field in model._meta.get_fields():
|
||||
if hasattr(field, 'attname') and field.get_internal_type() in ["CharField", "TextField", "EmailField"]:
|
||||
search_filters |= Q(**{f"{field.name}__icontains": query})
|
||||
return queryset.filter(search_filters).distinct()
|
||||
89
ofx_sample.ofx
Normal file
@ -0,0 +1,89 @@
|
||||
OFXHEADER:100
|
||||
DATA:OFXSGML
|
||||
VERSION:102
|
||||
SECURITY:NONE
|
||||
ENCODING:UTF-8
|
||||
CHARSET:NONE
|
||||
COMPRESSION:NONE
|
||||
OLDFILEUID:NONE
|
||||
NEWFILEUID:NONE
|
||||
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<OFX>
|
||||
<SIGNONMSGSRSV1>
|
||||
<SONRS>
|
||||
<STATUS>
|
||||
<CODE>0</CODE>
|
||||
<SEVERITY>INFO</SEVERITY>
|
||||
</STATUS>
|
||||
<DTSERVER>20250214000000</DTSERVER>
|
||||
<LANGUAGE>ENG</LANGUAGE>
|
||||
</SONRS>
|
||||
</SIGNONMSGSRSV1>
|
||||
<BANKMSGSRSV1>
|
||||
<STMTTRNRS>
|
||||
<TRNUID>1</TRNUID>
|
||||
<STATUS>
|
||||
<CODE>0</CODE>
|
||||
<SEVERITY>INFO</SEVERITY>
|
||||
</STATUS>
|
||||
<STMTRS>
|
||||
<CURDEF>USD</CURDEF>
|
||||
<BANKACCTFROM>
|
||||
<BANKID>123456789</BANKID>
|
||||
<ACCTID>987654321</ACCTID>
|
||||
<ACCTTYPE>CHECKING</ACCTTYPE>
|
||||
</BANKACCTFROM>
|
||||
<BANKTRANLIST>
|
||||
<DTSTART>20240214000000</DTSTART>
|
||||
<DTEND>20250213000000</DTEND>
|
||||
|
||||
<!-- Sample Transactions -->
|
||||
<STMTTRN>
|
||||
<TRNTYPE>DEP</TRNTYPE>
|
||||
<DTPOSTED>20240301090000</DTPOSTED>
|
||||
<TRNAMT>2500.00</TRNAMT>
|
||||
<FITID>100001</FITID>
|
||||
<NAME>PAYROLL DEPOSIT</NAME>
|
||||
</STMTTRN>
|
||||
|
||||
<STMTTRN>
|
||||
<TRNTYPE>POS</TRNTYPE>
|
||||
<DTPOSTED>20240305120000</DTPOSTED>
|
||||
<TRNAMT>-150.25</TRNAMT>
|
||||
<FITID>100002</FITID>
|
||||
<NAME>GROCERY STORE</NAME>
|
||||
</STMTTRN>
|
||||
|
||||
<STMTTRN>
|
||||
<TRNTYPE>ATM</TRNTYPE>
|
||||
<DTPOSTED>20240310080000</DTPOSTED>
|
||||
<TRNAMT>-100.00</TRNAMT>
|
||||
<FITID>100003</FITID>
|
||||
<NAME>ATM WITHDRAWAL</NAME>
|
||||
</STMTTRN>
|
||||
|
||||
<STMTTRN>
|
||||
<TRNTYPE>DEBIT</TRNTYPE>
|
||||
<DTPOSTED>20240402070000</DTPOSTED>
|
||||
<TRNAMT>-50.00</TRNAMT>
|
||||
<FITID>100004</FITID>
|
||||
<NAME>ONLINE SUBSCRIPTION</NAME>
|
||||
</STMTTRN>
|
||||
|
||||
<STMTTRN>
|
||||
<TRNTYPE>CHECK</TRNTYPE>
|
||||
<DTPOSTED>20240415090000</DTPOSTED>
|
||||
<TRNAMT>-500.00</TRNAMT>
|
||||
<FITID>100005</FITID>
|
||||
<NAME>RENT PAYMENT</NAME>
|
||||
</STMTTRN>
|
||||
</BANKTRANLIST>
|
||||
<LEDGERBAL>
|
||||
<BALAMT>7500.00</BALAMT>
|
||||
<DTASOF>20250213000000</DTASOF>
|
||||
</LEDGERBAL>
|
||||
</STMTRS>
|
||||
</STMTTRNRS>
|
||||
</BANKMSGSRSV1>
|
||||
</OFX>
|
||||
@ -53,11 +53,13 @@ django-extensions==3.2.3
|
||||
django-filter==24.3
|
||||
django-formtools==2.5.1
|
||||
django-ledger==0.7.4.1
|
||||
django-model-utils==5.0.0
|
||||
django-money==3.5.3
|
||||
django-next-url-mixin==0.4.0
|
||||
django-nine==0.2.7
|
||||
django-nonefield==0.4
|
||||
django-ordered-model==3.7.4
|
||||
django-pdf-actions==0.1.38
|
||||
django-phonenumber-field==8.0.0
|
||||
django-picklefield==3.2
|
||||
django-plans==1.2.0
|
||||
|
||||
BIN
static/assets/fonts/DejaVuSans.ttf
Normal file
BIN
static/assets/fonts/MarkaziText%5Bwght%5D.ttf
Normal file
BIN
static/assets/fonts/Roboto%5Bwdth,wght%5D.ttf
Normal file
BIN
static/images/car_make/Abarth_WQexAcP.png
Normal file
|
After Width: | Height: | Size: 46 KiB |
BIN
static/images/car_make/Alfa-Romeo-2_jj41jXd.png
Normal file
|
After Width: | Height: | Size: 132 KiB |
BIN
static/images/car_make/Aston-Martin_UVYOY98.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
static/images/car_make/Dodge.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
static/images/car_make/FAW.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
static/images/car_make/Ford.png
Normal file
|
After Width: | Height: | Size: 29 KiB |
BIN
static/images/car_make/Jeep.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
static/images/car_make/KIA.png
Normal file
|
After Width: | Height: | Size: 6.7 KiB |
BIN
static/images/car_make/KIA_RcrUDZf.png
Normal file
|
After Width: | Height: | Size: 7.8 KiB |
BIN
static/images/car_make/Maserati.png
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
static/images/car_make/Nissan.png
Normal file
|
After Width: | Height: | Size: 60 KiB |
BIN
static/images/car_make/Peugeot.png
Normal file
|
After Width: | Height: | Size: 40 KiB |
BIN
static/images/car_make/Toyota.png
Normal file
|
After Width: | Height: | Size: 73 KiB |
BIN
static/images/export_pdf/logos/logo-d-pdf.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
static/images/logos/users/Icon_Black4x_B0iEnf0.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
@ -46,6 +46,9 @@
|
||||
<div class="text-start mt-1">
|
||||
<a class="fs-9" href="{% url 'account_reset_password' %}">{{ _("Forgot Password?")}}</a>
|
||||
</div>
|
||||
|
||||
{% include 'partials/form_errors.html' %}
|
||||
|
||||
</form>
|
||||
<div class="text-center my-3 fs-9">
|
||||
{% trans 'If you have not created an account yet, then please' %}
|
||||
|
||||
@ -1,22 +1,38 @@
|
||||
<!-- crm/employee_calendar.html -->
|
||||
<h1>Upcoming Test Drives</h1>
|
||||
<table>
|
||||
{% extends 'base.html' %}
|
||||
{% load static %}
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="table-responsive border-translucent">
|
||||
<table class="table table-sm fs-9">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Customer</th>
|
||||
<th>Vehicle</th>
|
||||
<th>Date/Time</th>
|
||||
<th>Service</th>
|
||||
<th>Date</th>
|
||||
<th>Start Time</th>
|
||||
<th>End Time</th>
|
||||
<th>Staff</th>
|
||||
<th>Status</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for appointment in appointments %}
|
||||
<tr>
|
||||
<td>{{ appointment.client.name }}</td>
|
||||
<td>{{ appointment.service.name }}</td>
|
||||
<td>{{ appointment.start_time|date:"M j, Y H:i" }}</td>
|
||||
<td>{{ appointment.staff.user.get_full_name }}</td>
|
||||
<td>{{ appointment.get_client_name }}</td>
|
||||
<td>{{ appointment.get_service }}</td>
|
||||
<td>{{ appointment.appointment_request.date|date:"Y-m-d" }}</td>
|
||||
<td>{{ appointment.appointment_request.start_time }}</td>
|
||||
<td>{{ appointment.appointment_request.end_time }}</td>
|
||||
<td>{{ appointment.get_staff_member_name }}</td>
|
||||
<td></td>
|
||||
<td>
|
||||
<a href="{% url 'appointment:display_appointment' appointment.id %}">view</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@ -181,7 +181,7 @@
|
||||
<h3 class="mb-4" id="scrollspyNotes">{{ _("Notes") }}</h3>
|
||||
</div>
|
||||
<div class="d-flex align-items-center justify-content-start">
|
||||
<a id="addBtn" href="#" class="btn btn-sm btn-phoenix-primary mb-3" data-url="{% url 'add_note' lead.pk %}" data-bs-toggle="modal" data-bs-target="#noteModal" data-note-title="{{ _("Add") }}<i class='fa fa-plus-circle text-success ms-2'></i>">
|
||||
<a id="addBtn" href="#" class="btn btn-sm btn-phoenix-primary mb-3" data-url="{% url 'add_note_to_lead' lead.pk %}" data-bs-toggle="modal" data-bs-target="#noteModal" data-note-title="{{ _("Add") }}<i class='fa fa-plus-circle text-success ms-2'></i>">
|
||||
<span class="fas fa-plus me-1"></span>
|
||||
{% trans 'Add Note' %}
|
||||
</a>
|
||||
@ -197,7 +197,7 @@
|
||||
<th class="align-middle pe-0 text-end" scope="col" style="width:10%;"> </th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="list" id="lead-details-table-body">
|
||||
<tbody >
|
||||
{% for note in notes %}
|
||||
|
||||
<tr class="hover-actions-trigger btn-reveal-trigger position-static">
|
||||
@ -205,17 +205,17 @@
|
||||
{% if note.created_by.staff %}
|
||||
<td class="align-middle white-space-nowrap text-start white-space-nowrap">{{ note.created_by.staff.name }}</td>
|
||||
{% else %}
|
||||
<td class="align-middle white-space-nowrap text-start white-space-nowrap">{{ note.created_by.dealer.get_local_name }}</td>
|
||||
<td class="align-middle white-space-nowrap text-start white-space-nowrap">{{ note.created_by.dealer.get_local_name|default:note.created_by.dealer.name }}</td>
|
||||
{% endif %}
|
||||
<td class="align-middle text-body-tertiary text-start white-space-nowrap">{{ note.created }}</td>
|
||||
<td class="align-middle text-end white-space-nowrap pe-0 action py-2">
|
||||
{% if note.created_by == request.user %}
|
||||
<a id="updateBtn" href="#" class="btn btn-sm btn-phoenix-primary me-2" data-url="{% url 'update_note' note.pk %}" data-bs-toggle="modal" data-bs-target="#noteModal" data-note-title="{{ _("Update") }}<i class='fas fa-pen-square text-primary ms-2'></i>">
|
||||
<a id="updateBtn" href="#" class="btn btn-sm btn-phoenix-primary me-2" data-url="{% url 'update_note_to_lead' note.pk %}" data-bs-toggle="modal" data-bs-target="#noteModal" data-note-title="{{ _("Update") }}<i class='fas fa-pen-square text-primary ms-2'></i>">
|
||||
<i class="fas fa-pen"></i>
|
||||
</a>
|
||||
|
||||
<button class="btn btn-phoenix-danger btn-sm delete-btn"
|
||||
data-url="{% url 'delete_note' note.pk %}"
|
||||
data-url="{% url 'delete_note_to_lead' note.pk %}"
|
||||
data-message="Are you sure you want to delete this note?"
|
||||
data-bs-toggle="modal" data-bs-target="#deleteModal">
|
||||
<i class="fas fa-trash"></i>
|
||||
|
||||
@ -121,8 +121,7 @@
|
||||
</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"><a href="#!">
|
||||
</a>
|
||||
<div class="d-flex align-items-center">
|
||||
<div><a class="fs-8 fw-bold" href="{% url 'lead_detail' lead.pk %}">{{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>
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
{% load i18n static crispy_forms_filters %}
|
||||
|
||||
{% if form.instance.pk %}
|
||||
<form method="post" action="{% url 'update_note' note.pk %}" enctype="multipart/form-data">
|
||||
<form method="post" action="{% url 'update_note_to_lead' note.pk %}" enctype="multipart/form-data">
|
||||
{% else %}
|
||||
<form method="post" action="{% url 'add_note' lead.pk %}" enctype="multipart/form-data">
|
||||
<form method="post" action="{% url 'add_note_to_lead' lead.pk %}" enctype="multipart/form-data">
|
||||
{% endif %}
|
||||
|
||||
{% csrf_token %}
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<h2 class="mb-4">{{ _("Customers")|capfirst }}</h2>
|
||||
<h4 class="mb-4">{{ _("Customers")|capfirst }}</h4>
|
||||
<div class="row g-3 justify-content-between mb-4">
|
||||
<div class="col-auto">
|
||||
<div class="d-md-flex justify-content-between">
|
||||
@ -17,25 +17,15 @@
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<div class="d-flex">
|
||||
<div class="search-box me-2">
|
||||
<form method="get" class="d-inline-block position-relative">
|
||||
<div class="input-group">
|
||||
<button type="submit" class="btn btn-phoenix-primary"><span class="fas fa-search search-box-icon"></span></button>
|
||||
<input name="q" class="form-control search-input search" type="search" placeholder="{{ _('Enter customer name') }}" value="{{ request.GET.q }}"/>
|
||||
{% if request.GET.q %}
|
||||
<a href="{% url request.resolver_match.view_name %}" class="btn btn-close"></a>
|
||||
</div>
|
||||
{% endif %}
|
||||
</form>
|
||||
</div>
|
||||
{% include 'partials/search_box.html' %}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% if page_obj.object_list %}
|
||||
<div class="table-responsive scrollbar mx-n1 px-1">
|
||||
<div class="table-responsive scrollbar transition">
|
||||
|
||||
<table class="table fs-9 mb-0 border-top border-translucent">
|
||||
<table class="table table-sm fs-9 mb-0 border-translucent">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
@ -104,14 +94,10 @@
|
||||
{% endif %}
|
||||
</table>
|
||||
</div>
|
||||
<div class="row align-items-center justify-content-end py-4 pe-0 fs-9">
|
||||
|
||||
{% include 'partials/pagination.html' %}
|
||||
|
||||
<!-- Optional: Pagination -->
|
||||
{% if is_paginated %}
|
||||
{% include 'partials/pagination.html' %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% include 'modal/delete_modal.html' %}
|
||||
|
||||
{% endblock %}
|
||||
@ -106,7 +106,10 @@
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="d-flex align-items-center justify-content-end">
|
||||
<a class="fw-bold fs-9 mb-0 text-end" href="{% url 'add_note_to_customer' customer.pk %}"><span class="fas fa-plus me-1"></span>{{ _("Add Note")}}</a>
|
||||
<a id="addBtn" href="#" class="btn btn-sm btn-phoenix-primary mb-3" data-url="{% url 'add_note_to_customer' customer.pk %}" data-bs-toggle="modal" data-bs-target="#noteModal" data-note-title="{{ _("Add") }}<i class='fa fa-plus-circle text-success ms-2'></i>">
|
||||
<span class="fas fa-plus me-1"></span>
|
||||
{% trans 'Add Note' %}
|
||||
</a>
|
||||
</div>
|
||||
<table class="table fs-9 mb-0 table-responsive">
|
||||
<thead>
|
||||
@ -214,21 +217,13 @@
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="row align-items-center justify-content-between py-2 pe-0 fs-9">
|
||||
<div class="col-auto d-flex">
|
||||
<p class="mb-0 d-none d-sm-block me-3 fw-semibold text-body" data-list-info="data-list-info"></p><a class="fw-semibold" href="#!" data-list-view="*">View all<span class="fas fa-angle-right ms-1" data-fa-transform="down-1"></span></a><a class="fw-semibold d-none" href="#!" data-list-view="less">View Less<span class="fas fa-angle-right ms-1" data-fa-transform="down-1"></span></a>
|
||||
</div>
|
||||
<div class="col-auto d-flex">
|
||||
<button class="page-link" data-list-pagination="prev"><span class="fas fa-chevron-left"></span></button>
|
||||
<ul class="mb-0 pagination"></ul>
|
||||
<button class="page-link pe-0" data-list-pagination="next"><span class="fas fa-chevron-right"></span></button>
|
||||
</div>
|
||||
</div>
|
||||
{% include 'partials/pagination.html' %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% include 'partials/notes.html' %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
@ -436,15 +436,17 @@
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="nav-item">
|
||||
<a class="nav-link px-3 d-block" href="{% url 'user_detail' user.staff.pk %}"> <span class="me-2 text-body align-bottom" data-feather="user"></span><span>{% translate 'profile'|capfirst %}</span></a>
|
||||
<a class="nav-link px-3 d-block" href="{% url 'appointment:user_profile' request.user.id %}"> <span class="me-2 text-body align-bottom" data-feather="user"></span><span>{% translate 'profile'|capfirst %}</span></a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if user.dealer %}
|
||||
<li class="nav-item">
|
||||
<a class="nav-link px-3 d-block" href="{% url 'user_list' %}"><span class="me-2 text-body align-bottom" data-feather="users"></span>{{ _("Staff") }}</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link px-3 d-block" href="{% url 'dealer_activity' %}"> <span class="me-2 text-body align-bottom" data-feather="lock"></span>{{ _("Activity") }}</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
<li class="nav-item">
|
||||
<a class="nav-link px-3 d-block" href=""> <span class="me-2 text-body align-bottom" data-feather="settings"></span>Settings & Privacy </a>
|
||||
</li>
|
||||
|
||||
@ -2,6 +2,8 @@
|
||||
{% load i18n static %}
|
||||
|
||||
{% block content %}
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
|
||||
|
||||
<div class="px-3 mb-6">
|
||||
<div class="row justify-content-between">
|
||||
@ -46,7 +48,7 @@
|
||||
<div class="border-translucent mb-sm-4">
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="d-flex align-items-center icon-wrapper-sm shadow-primary-100" style="transform: rotate(-7.45deg);">
|
||||
<div class="icon icon-shape bg-gradient-info text-white rounded-circle shadow">MMMMMMMMMMMMMMM</div>
|
||||
<div class="icon icon-shape bg-gradient-info text-white rounded-circle shadow"></div>
|
||||
</div>
|
||||
<p class="text-body-tertiary fs-9 mb-0 ms-2 mt-3">Outgoing call</p>
|
||||
</div>
|
||||
@ -71,7 +73,13 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6 col-md-4 col-xl-3 col-xxl-4 align-content-center">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="row g-3 mb-3">
|
||||
<canvas id="pnlChart"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -103,4 +111,55 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
const url = "{% url 'django_ledger:entity-json-pnl' dealer.entity.slug %}"
|
||||
async function fetchPnLData() {
|
||||
try {
|
||||
const response = await fetch(url); // Update with your API URL
|
||||
const jsonResponse = await response.json();
|
||||
const pnlData = jsonResponse.results.pnl_data;
|
||||
console.log(pnlData);
|
||||
|
||||
const labels = Object.keys(pnlData);
|
||||
const profits = labels.map(label => pnlData[label].GROUP_NET_PROFIT);
|
||||
const sales = labels.map(label => pnlData[label].GROUP_NET_SALES);
|
||||
|
||||
renderChart(labels, profits, sales);
|
||||
} catch (error) {
|
||||
console.error("Error fetching PnL data:", error);
|
||||
}
|
||||
}
|
||||
|
||||
function renderChart(labels, profits, sales) {
|
||||
const ctx = document.getElementById("pnlChart").getContext("2d");
|
||||
new Chart(ctx, {
|
||||
type: "bar",
|
||||
data: {
|
||||
labels: labels,
|
||||
datasets: [
|
||||
{
|
||||
label: "Net Profit",
|
||||
data: profits,
|
||||
backgroundColor: "green"
|
||||
},
|
||||
{
|
||||
label: "Net Sales",
|
||||
data: sales,
|
||||
backgroundColor: "blue"
|
||||
}
|
||||
]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
scales: {
|
||||
y: {
|
||||
beginAtZero: true
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fetchPnLData();
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
@ -38,144 +38,121 @@
|
||||
<li class="nav-item"><a class="nav-link px-2 py-1" href="{% url 'car_list' %}?status=reserved"><span>Reserved</span><span class="text-body-tertiary fw-semibold">({{stats.reserved}})</span></a></li>
|
||||
<li class="nav-item"><a class="nav-link px-2 py-1" href="{% url 'car_list' %}?status=transfer"><span>Transfer</span><span class="text-body-tertiary fw-semibold">({{stats.transfer}})</span></a></li>
|
||||
<li class="nav-item"><a class="nav-link px-2 py-1" href="{% url 'car_list' %}?status=sold"><span>Sold</span><span class="text-body-tertiary fw-semibold">({{stats.sold}})</span></a></li>
|
||||
<li class="nav-item"><button hx-on:click="toggle_filter()" class="nav-link px-2 py-1"><span>Filter</span><span class="text-body-tertiary fw-semibold"></span></button></li>
|
||||
<li class="nav-item"><button hx-on:click="toggle_filter()" class="btn btn-sm btn-primary px-2 py-1"><span><span class="fa fa-filter me-1"></span>{{ _("Filter") }}</span><span class="fas fa-caret-down fs-9 ms-1 filter-icon"></span></button></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col-12 col-sm-auto">
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="spinner-border mx-3 htmx-indicator" role="status"><span class="visually-hidden">Loading...</span></div>
|
||||
<div class="search-box me-3">
|
||||
<div class="search-box me-3">
|
||||
<form class="position-relative">
|
||||
<input class="form-control search-input search" name='search' type="search" placeholder="Search" aria-label="Search" hx-get="{% url 'car_list' %}" hx-trigger='keyup changed delay:500ms' hx-target='.table-responsive' hx-select='.table-responsive' hx-swap="innerHTML show:window:top" hx-indicator=".htmx-indicator"
|
||||
<input class="form-control search-input search" name='search' type="search" placeholder="Search" aria-label="Search" hx-get="{% url 'car_list' %}" hx-trigger='keyup changed delay:500ms' hx-target='.table-responsive' hx-select='.table-responsive' hx-swap="innerHTML show:window:top" hx-indicator=".htmx-indicator"
|
||||
hx-on::before-request="on_before_request()"
|
||||
hx-on::after-request="on_after_request()"
|
||||
/>
|
||||
/>
|
||||
<span class="fas fa-search search-box-icon"></span>
|
||||
</form>
|
||||
</div><a class="btn btn-phoenix-primary px-3 me-1 border-0 text-body" href="#" data-bs-toggle="tooltip" data-bs-placement="top" data-bs-title="List view"><span class="fa-solid fa-list fs-10"></span></a><a class="btn btn-phoenix-primary px-3 me-1" href="#" data-bs-toggle="tooltip" data-bs-placement="top" data-bs-title="Board view">
|
||||
<svg width="9" height="9" viewbox="0 0 9 9" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M0 0.5C0 0.223857 0.223858 0 0.5 0H1.83333C2.10948 0 2.33333 0.223858 2.33333 0.5V1.83333C2.33333 2.10948 2.10948 2.33333 1.83333 2.33333H0.5C0.223857 2.33333 0 2.10948 0 1.83333V0.5Z" fill="currentColor"></path>
|
||||
<path d="M3.33333 0.5C3.33333 0.223857 3.55719 0 3.83333 0H5.16667C5.44281 0 5.66667 0.223858 5.66667 0.5V1.83333C5.66667 2.10948 5.44281 2.33333 5.16667 2.33333H3.83333C3.55719 2.33333 3.33333 2.10948 3.33333 1.83333V0.5Z" fill="currentColor"></path>
|
||||
<path d="M6.66667 0.5C6.66667 0.223857 6.89052 0 7.16667 0H8.5C8.77614 0 9 0.223858 9 0.5V1.83333C9 2.10948 8.77614 2.33333 8.5 2.33333H7.16667C6.89052 2.33333 6.66667 2.10948 6.66667 1.83333V0.5Z" fill="currentColor"></path>
|
||||
<path d="M0 3.83333C0 3.55719 0.223858 3.33333 0.5 3.33333H1.83333C2.10948 3.33333 2.33333 3.55719 2.33333 3.83333V5.16667C2.33333 5.44281 2.10948 5.66667 1.83333 5.66667H0.5C0.223857 5.66667 0 5.44281 0 5.16667V3.83333Z" fill="currentColor"></path>
|
||||
<path d="M3.33333 3.83333C3.33333 3.55719 3.55719 3.33333 3.83333 3.33333H5.16667C5.44281 3.33333 5.66667 3.55719 5.66667 3.83333V5.16667C5.66667 5.44281 5.44281 5.66667 5.16667 5.66667H3.83333C3.55719 5.66667 3.33333 5.44281 3.33333 5.16667V3.83333Z" fill="currentColor"></path>
|
||||
<path d="M6.66667 3.83333C6.66667 3.55719 6.89052 3.33333 7.16667 3.33333H8.5C8.77614 3.33333 9 3.55719 9 3.83333V5.16667C9 5.44281 8.77614 5.66667 8.5 5.66667H7.16667C6.89052 5.66667 6.66667 5.44281 6.66667 5.16667V3.83333Z" fill="currentColor"></path>
|
||||
<path d="M0 7.16667C0 6.89052 0.223858 6.66667 0.5 6.66667H1.83333C2.10948 6.66667 2.33333 6.89052 2.33333 7.16667V8.5C2.33333 8.77614 2.10948 9 1.83333 9H0.5C0.223857 9 0 8.77614 0 8.5V7.16667Z" fill="currentColor"></path>
|
||||
<path d="M3.33333 7.16667C3.33333 6.89052 3.55719 6.66667 3.83333 6.66667H5.16667C5.44281 6.66667 5.66667 6.89052 5.66667 7.16667V8.5C5.66667 8.77614 5.44281 9 5.16667 9H3.83333C3.55719 9 3.33333 8.77614 3.33333 8.5V7.16667Z" fill="currentColor"></path>
|
||||
<path d="M6.66667 7.16667C6.66667 6.89052 6.89052 6.66667 7.16667 6.66667H8.5C8.77614 6.66667 9 6.89052 9 7.16667V8.5C9 8.77614 8.77614 9 8.5 9H7.16667C6.89052 9 6.66667 8.77614 6.66667 8.5V7.16667Z" fill="currentColor"></path>
|
||||
</svg></a><a class="btn btn-phoenix-primary px-3" href="#" data-bs-toggle="tooltip" data-bs-placement="top" data-bs-title="Card view">
|
||||
<svg width="9" height="9" viewBox="0 0 9 9" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M0 0.5C0 0.223858 0.223858 0 0.5 0H3.5C3.77614 0 4 0.223858 4 0.5V3.5C4 3.77614 3.77614 4 3.5 4H0.5C0.223858 4 0 3.77614 0 3.5V0.5Z" fill="currentColor"></path>
|
||||
<path d="M0 5.5C0 5.22386 0.223858 5 0.5 5H3.5C3.77614 5 4 5.22386 4 5.5V8.5C4 8.77614 3.77614 9 3.5 9H0.5C0.223858 9 0 8.77614 0 8.5V5.5Z" fill="currentColor"></path>
|
||||
<path d="M5 0.5C5 0.223858 5.22386 0 5.5 0H8.5C8.77614 0 9 0.223858 9 0.5V3.5C9 3.77614 8.77614 4 8.5 4H5.5C5.22386 4 5 3.77614 5 3.5V0.5Z" fill="currentColor"></path>
|
||||
<path d="M5 5.5C5 5.22386 5.22386 5 5.5 5H8.5C8.77614 5 9 5.22386 9 5.5V8.5C9 8.77614 8.77614 9 8.5 9H5.5C5.22386 9 5 8.77614 5 8.5V5.5Z" fill="currentColor"></path>
|
||||
</svg></a>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex align-items-center d-none filter">
|
||||
<select hx-get="{% url 'car_list' %}" name="make" hx-target='.model-select' hx-select='.model-select' hx-swap="outerHTML show:window:top" hx-indicator=".htmx-indicator" class="form-select make" aria-label="Default select example"
|
||||
<select hx-get="{% url 'car_list' %}" name="make" hx-target='.model-select' hx-select='.model-select' hx-swap="outerHTML show:window:top" hx-indicator=".htmx-indicator" class="form-select form-control-sm me-1 make" aria-label="Default select example"
|
||||
hx-on::before-request="filter_before_request()"
|
||||
hx-on::after-request="filter_after_request()"
|
||||
>
|
||||
<option selected="" value="" disabled>Select Make</option>
|
||||
hx-on::after-request="filter_after_request()">
|
||||
<option selected="" value="" disabled>{{ _("Make")}}</option>
|
||||
{% for m in make %}
|
||||
<option value="{{ m.pk }}">{{ m.name }}</option>
|
||||
<option value="{{ m.pk }}">{{ m.get_local_name|default:m.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<select hx-get="{% url 'car_list' %}" hx-include=".make" name="model" hx-target='.year' hx-select='.year' hx-swap="outerHTML show:window:top" hx-indicator=".htmx-indicator" class="form-select model-select" aria-label="Default select example"
|
||||
<select hx-get="{% url 'car_list' %}" hx-include=".make" name="model" hx-target='.year' hx-select='.year' hx-swap="outerHTML show:window:top" hx-indicator=".htmx-indicator" class="form-select form-control-sm me-1 model-select" aria-label="Default select example"
|
||||
hx-on::before-request="filter_before_request()"
|
||||
hx-on::after-request="filter_after_request()"
|
||||
>
|
||||
<option selected="" value="" disabled>Select Model</option>
|
||||
<option selected="" value="" disabled>{{ _("Model")}}</option>
|
||||
{% for m in model %}
|
||||
<option value="{{ m.pk }}">{{ m.name }}</option>
|
||||
<option value="{{ m.pk }}">{{ m.get_local_name|default:m.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<select class="form-select year" name="year" aria-label="Default select example">
|
||||
<option selected="" value="" disabled>Select Year</option>
|
||||
<select class="form-select form-control-sm me-1 year" name="year" aria-label="Default select example">
|
||||
<option selected="" value="" disabled>{{ _("Year")}}</option>
|
||||
{% for y in year %}
|
||||
<option value="{{ y.0 }}">{{ y.0 }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<select class="form-select car_status" name="car_status" aria-label="Default select example">
|
||||
<option selected="" value="">All</option>
|
||||
<option value="available">Available</option>
|
||||
<option value="reserved">Reserved</option>
|
||||
<option value="sold">Sold</option>
|
||||
<option value="transfer">Transfer</option>
|
||||
<select class="form-select form-control-sm me-1 car_status" name="car_status" aria-label="Default select example">
|
||||
<option selected="" value="">{{ _("All") }}</option>
|
||||
<option value="available">{{ _("Available") }}</option>
|
||||
<option value="reserved">{{ _("Reserved") }}</option>
|
||||
<option value="sold">{{ _("Sold") }}</option>
|
||||
<option value="transfer">{{ _("Transfer") }}</option>
|
||||
</select>
|
||||
<button id="search" hx-get="{% url 'car_list' %}" hx-include=".make,.model,.year,.car_status" hx-indicator=".htmx-indicator" hx-target='.table-responsive' hx-select='.table-responsive' hx-swap="outerHTML show:window:top" class="btn btn-sm btn-phoenix-primary"
|
||||
<button id="search" hx-get="{% url 'car_list' %}" hx-include=".make,.model,.year,.car_status" hx-indicator=".htmx-indicator" hx-target='.table-responsive' hx-select='.table-responsive' hx-swap="outerHTML show:window:top" class="btn btn-sm btn-phoenix-primary ms-1"
|
||||
hx-on::before-request="filter_before_request()"
|
||||
hx-on::after-request="filter_after_request()">Search</button>
|
||||
hx-on::after-request="filter_after_request()">{{ _("Search") }}</button>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="table-responsive scrollbar transition">
|
||||
<div class="d-flex flex-wrap align-items-center justify-content-between py-3 pe-0 fs-9 border-bottom border-translucent">
|
||||
<div class="d-flex">
|
||||
</div>
|
||||
<div class="d-flex flex-wrap align-items-center justify-content-between py-3 pe-0 fs-9">
|
||||
<div class="d-flex" hx-boost="true" hx-push-url='false' hx-include=".make,.model,.year,.car_status" hx-target=".table-responsive" hx-select=".table-responsive" hx-swap="innerHTML show:window:top" hx-indicator=".htmx-indicator"
|
||||
hx-on::before-request="on_before_request()"
|
||||
hx-on::after-request="on_after_request()">
|
||||
{% if page_obj.has_previous %}
|
||||
<a href="{% url 'car_list' %}?page={{page_obj.previous_page_number}}" class="page-link" data-list-pagination="prev"><span class="fas fa-chevron-left"></span></a>
|
||||
{% endif %}
|
||||
<ul class="mb-0 pagination">Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}</ul>
|
||||
{% if page_obj.has_next %}
|
||||
<a href="{% url 'car_list' %}?page={{page_obj.next_page_number}}" class="page-link pe-0" data-list-pagination="next"><span class="fas fa-chevron-right"></span></a>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<table class="table fs-9 mb-0 border-top border-translucent">
|
||||
|
||||
|
||||
<table class="table table-sm fs-9 mb-0 border-translucent">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="sort white-space-nowrap align-middle ps-0" scope="col" data-sort="projectName" style="width:10%;">Make</th>
|
||||
<th class="sort align-middle ps-3" scope="col" data-sort="assignees" style="width:10%;">Model</th>
|
||||
<th class="sort align-middle ps-3" scope="col" data-sort="start" style="width:10%;">Year</th>
|
||||
<th class="sort align-middle ps-3" scope="col" data-sort="deadline" style="width:15%;">Trim</th>
|
||||
<th class="sort align-middle ps-3" scope="col" data-sort="task" style="width:12%;">VIN</th>
|
||||
<th class="sort align-middle ps-3" scope="col" data-sort="task" style="width:12%;">Receiving Date</th>
|
||||
<th class="sort white-space-nowrap align-middle ps-0" scope="col" data-sort="projectName" style="width:10%;">
|
||||
{{ _("Make") }}</th>
|
||||
<th class="sort align-middle ps-3" scope="col" data-sort="assignees" style="width:10%;">{{ _("Model") }}</th>
|
||||
<th class="sort align-middle ps-3" scope="col" data-sort="start" style="width:10%;">{{ _("Year") }}</th>
|
||||
<th class="sort align-middle ps-3" scope="col" data-sort="deadline" style="width:15%;">{{ _("Trim") }}</th>
|
||||
<th class="sort align-middle ps-3" scope="col" data-sort="task" style="width:12%;">{{ _("VIN") }}</th>
|
||||
<th class="sort align-middle ps-3" scope="col" data-sort="task" style="width:12%;">{{ _("Age") }}</th>
|
||||
|
||||
<th class="sort align-middle text-end" scope="col" data-sort="statuses" style="width:10%;">STATUS</th>
|
||||
<th class="sort align-middle text-end" scope="col" data-sort="statuses" style="width:10%;">{{ _("Status") }}</th>
|
||||
<th class="sort align-middle text-end" scope="col" style="width:10%;"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="list" id="project-list-table-body">
|
||||
{% for car in cars %}
|
||||
{% for car in page_obj %}
|
||||
<tr class="position-static">
|
||||
<td class="align-middle time white-space-nowrap ps-0 projectName py-4"><a class="fw-bold fs-8" href="{% url 'car_detail' car.pk %}">{{car.id_car_make}}</a></td>
|
||||
<td class="align-middle white-space-nowrap start ps-3 py-4">
|
||||
<p class="mb-0 fs-9 text-body">{{car.id_car_model}}</p>
|
||||
<td class="align-middle time white-space-nowrap ps-0 projectName"><a class="fw-bold fs-8" href="{% url 'car_detail' car.pk %}">{{car.id_car_make.get_local_name|default:car.id_car_make.name}}</a></td>
|
||||
<td class="align-middle white-space-nowrap start">
|
||||
<p class="mb-0 fs-9 text-body">{{car.id_car_model.get_local_name|default:car.id_car_model.name}}</p>
|
||||
</td>
|
||||
<td class="align-middle white-space-nowrap deadline ps-3 py-4">
|
||||
<td class="align-middle white-space-nowrap deadline">
|
||||
<p class="mb-0 fs-9 text-body">{{car.year}}</p>
|
||||
</td>
|
||||
<td class="align-middle white-space-nowrap task ps-3 py-4">
|
||||
<p class="fw-bo text-body fs-9 mb-0">{{car.id_car_trim}}</p>
|
||||
<td class="align-middle white-space-nowrap task">
|
||||
<p class="fw-bo text-body fs-9 mb-0">{{car.id_car_trim }}</p>
|
||||
</td>
|
||||
<td class="align-middle white-space-nowrap task ps-3 py-4">
|
||||
<td class="align-middle white-space-nowrap task">
|
||||
<p class="fw-bo text-body fs-9 mb-0">{{car.vin}}</p>
|
||||
</td>
|
||||
<td class="align-middle white-space-nowrap task ps-3 py-4">
|
||||
<p class="fw-bo text-body fs-9 mb-0">{{car.receiving_date}}</p>
|
||||
<td class="align-middle white-space-nowrap task">
|
||||
<p class="fw-bo text-body fs-9 mb-0">{{car.receiving_date|timesince}}</p>
|
||||
</td>
|
||||
|
||||
<td class="align-middle white-space-nowrap text-end statuses">
|
||||
{% if car.status == "available" %}
|
||||
<span class="badge badge-phoenix fs-10 badge-phoenix-success">{{car.status}}</span>
|
||||
<span class="badge badge-phoenix fs-11 badge-phoenix-success">{{car.status}}</span>
|
||||
{% elif car.status == "reserved" %}
|
||||
<span class="badge badge-phoenix fs-10 badge-phoenix-danger">{{car.status}}</span>
|
||||
<span class="badge badge-phoenix fs-11 badge-phoenix-danger">{{car.status}}</span>
|
||||
{% elif car.status == "sold" %}
|
||||
<span class="badge badge-phoenix fs-10 badge-phoenix-info">{{car.status}}</span>
|
||||
<span class="badge badge-phoenix fs-11 badge-phoenix-info">{{car.status}}</span>
|
||||
{% elif car.status == "transfer" %}
|
||||
<span class="badge badge-phoenix fs-10 badge-phoenix-warning">{{car.status}}</span>
|
||||
<span class="badge badge-phoenix fs-11 badge-phoenix-warning">{{car.status}}</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="align-middle text-end white-space-nowrap pe-0 action">
|
||||
<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 class="dropdown-item" href="#!">View</a><a class="dropdown-item" href="#!">Export</a>
|
||||
<div class="dropdown-divider"></div><a class="dropdown-item text-danger" href="#!">Remove</a>
|
||||
<div class="dropdown-menu dropdown-menu-end py-2">
|
||||
<a class="dropdown-item" href="#!">{{ _("View") }}</a>
|
||||
<a class="dropdown-item" href="#!">{{ _("Export") }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
@ -183,23 +160,16 @@
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div class="d-flex flex-wrap align-items-center justify-content-between py-3 pe-0 fs-9 border-bottom border-translucent">
|
||||
<div class="d-flex">
|
||||
<p class="mb-0 d-none d-sm-block me-3 fw-semibold text-body" data-list-info="data-list-info"></p><a class="fw-semibold" href="#!" data-list-view="*">View all<span class="fas fa-angle-right ms-1" data-fa-transform="down-1"></span></a><a class="fw-semibold d-none" href="#!" data-list-view="less">View Less<span class="fas fa-angle-right ms-1" data-fa-transform="down-1"></span></a>
|
||||
</div>
|
||||
<div class="d-flex" hx-boost="true" hx-push-url='false' hx-include=".make,.model,.year,.car_status" hx-target=".table-responsive" hx-select=".table-responsive" hx-swap="innerHTML" hx-indicator=".htmx-indicator"
|
||||
hx-on::before-request="on_before_request()"
|
||||
hx-on::after-request="on_after_request()">
|
||||
{% if page_obj.has_previous %}
|
||||
<a href="{% url 'car_list' %}?page={{page_obj.previous_page_number}}" class="page-link" data-list-pagination="prev"><span class="fas fa-chevron-left"></span></a>
|
||||
{% endif %}
|
||||
<ul class="mb-0 pagination">Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}</ul>
|
||||
{% if page_obj.has_next %}
|
||||
<a href="{% url 'car_list' %}?page={{page_obj.next_page_number}}" class="page-link pe-0" data-list-pagination="next"><span class="fas fa-chevron-right"></span></a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% include 'partials/pagination.html' %}
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -225,7 +195,9 @@
|
||||
document.querySelector('.model-select').classList.remove('on-after-request')
|
||||
}
|
||||
function toggle_filter(){
|
||||
document.querySelector('.filter').classList.toggle('d-none')
|
||||
document.querySelector('.filter').classList.toggle('d-none')
|
||||
document.querySelector('.filter-icon').classList.toggle("fa-caret-down");
|
||||
document.querySelector('.filter-icon').classList.toggle("fa-caret-up");
|
||||
}
|
||||
function filter_before_request(){
|
||||
document.querySelector('.model-select').setAttribute('disabled', true)
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
<div class="row">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-8">
|
||||
<div class="card">
|
||||
<div class="card bg-body">
|
||||
<div class="card-header">
|
||||
{% if service.pk %}
|
||||
{{ _("Update Service") }}
|
||||
@ -19,7 +19,7 @@
|
||||
<form method="post" action="">
|
||||
{% csrf_token %}
|
||||
{{ form|crispy }}
|
||||
<button type="submit" class="btn btn-primary">{% trans 'Save' %}</button>
|
||||
<button type="submit" class="btn btn-sm btn-primary">{% trans 'Save' %}</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -4,16 +4,16 @@
|
||||
{% block title %}{{ _("Expenses") }}{% endblock title %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row mt-4 mx-4">
|
||||
<div class="d-flex justify-content-between mb-2 p-6">
|
||||
<span></span>
|
||||
<h3 class="text-center">{% trans "Services" %}</h3>
|
||||
<a href="{% url 'item_service_create' %}" class="btn btn-sm btn-success ">{% trans "Add Service" %}</a>
|
||||
<div class="row">
|
||||
<div class="d-flex justify-content-between">
|
||||
|
||||
<h3 class="mb-2">{% trans "Services" %}</h3>
|
||||
<a href="{% url 'item_service_create' %}" class="btn btn-sm btn-phoenix-primary ">{% trans "Add Service" %}</a>
|
||||
</div>
|
||||
<div class="mx-n4 px-4 mx-lg-n6 px-lg-6 bg-body-emphasis pt-7 border-y">
|
||||
|
||||
|
||||
<div class="table-responsive mx-n1 px-1 scrollbar">
|
||||
<table class="table fs-9 mb-0 border-top border-translucent">
|
||||
<div class="table-responsive scrollbar transition">
|
||||
<table class="table table-sm fs-9 mb-0 border-translucent">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="sort white-space-nowrap align-middle" scope="col">{% trans "Item Number" %}</th>
|
||||
@ -56,5 +56,4 @@
|
||||
<div class="d-flex justify-content-center">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@ -1,6 +1,6 @@
|
||||
<!-- Display Form Errors -->
|
||||
{% if form.errors %}
|
||||
<div class="alert alert-danger">
|
||||
<div class="alert alert-danger alert-dismissible fade show" role="alert">
|
||||
<ul>
|
||||
{% for field in form %}
|
||||
{% for error in field.errors %}
|
||||
|
||||
@ -1,65 +1,39 @@
|
||||
{% load i18n static %}
|
||||
|
||||
<div class="pagination my-1">
|
||||
<nav aria-label="Page navigation">
|
||||
<ul class="pagination pagination-sm justify-content-center mb-0">
|
||||
{% if page_obj.has_previous %}
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="?page={{ page_obj.previous_page_number }}" aria-label="{% trans 'Previous' %}">
|
||||
<span aria-hidden="true">
|
||||
{% if LANGUAGE_CODE == 'ar' %}
|
||||
<span class="fa fa-chevron-right"></span>
|
||||
{% else %}
|
||||
<span class="fa fa-chevron-left"></span>
|
||||
{% endif %}
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="page-item disabled">
|
||||
<span class="page-link" aria-hidden="true">
|
||||
{% if LANGUAGE_CODE == 'ar' %}
|
||||
<span class="fa fa-chevron-right"></span>
|
||||
{% else %}
|
||||
<span class="fa fa-chevron-left"></span>
|
||||
{% endif %}
|
||||
</span>
|
||||
</li>
|
||||
{% endif %}
|
||||
<div class="row align-items-center justify-content-end py-4 pe-0 fs-9">
|
||||
<div class="col-auto d-flex">
|
||||
{% if page_obj.has_previous %}
|
||||
<button class="page-link" data-list-pagination="prev" onclick="window.location.href='?page={{ page_obj.previous_page_number }}'">
|
||||
<span class="fas {% if LANGUAGE_CODE == 'ar' %}fa-chevron-right{% else %}fa-chevron-left{% endif %}"></span>
|
||||
</button>
|
||||
{% else %}
|
||||
<button class="page-link" disabled>
|
||||
<span class="fas {% if LANGUAGE_CODE == 'ar' %}fa-chevron-right{% else %}fa-chevron-left{% endif %}"></span>
|
||||
</button>
|
||||
{% endif %}
|
||||
|
||||
<ul class="mb-0 pagination">
|
||||
{% for num in page_obj.paginator.page_range %}
|
||||
{% if page_obj.number == num %}
|
||||
<li class="page-item active">
|
||||
<span class="page-link">{{ num }}</span>
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="?page={{ num }}">{{ num }}</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if page_obj.number == num %}
|
||||
<li class="page-item active">
|
||||
<span class="page-link">{{ num }}</span>
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="?page={{ num }}">{{ num }}</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% if page_obj.has_next %}
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="?page={{ page_obj.next_page_number }}" aria-label="{% trans 'Next' %}">
|
||||
<span aria-hidden="true">
|
||||
{% if LANGUAGE_CODE == 'ar' %}
|
||||
<span class="fa fa-chevron-left"></span>
|
||||
{% else %}
|
||||
<span class="fa fa-chevron-right"></span>
|
||||
{% endif %}
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="page-item disabled">
|
||||
<span class="page-link" aria-hidden="true">
|
||||
{% if LANGUAGE_CODE == 'ar' %}
|
||||
<span class="fa fa-chevron-left"></span>
|
||||
{% else %}
|
||||
<span class="fa fa-chevron-right"></span>
|
||||
{% endif %}
|
||||
</span>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
{% if page_obj.has_next %}
|
||||
<button class="page-link pe-0" data-list-pagination="next" onclick="window.location.href='?page={{ page_obj.next_page_number }}'">
|
||||
<span class="fas {% if LANGUAGE_CODE == 'ar' %}fa-chevron-left{% else %}fa-chevron-right{% endif %}"></span>
|
||||
</button>
|
||||
{% else %}
|
||||
<button class="page-link pe-0" disabled>
|
||||
<span class="fas {% if LANGUAGE_CODE == 'ar' %}fa-chevron-left{% else %}fa-chevron-right{% endif %}"></span>
|
||||
</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
15
templates/partials/search_box.html
Normal file
@ -0,0 +1,15 @@
|
||||
<div class="search-box me-2">
|
||||
<form class="position-relative justify-content-between">
|
||||
|
||||
<input name="q" class="form-control form-control-sm search-input search" type="search" aria-label="Search" placeholder="{{ _('Search') }}" value="{{ request.GET.q }}"/>
|
||||
<span class="fa fa-magnifying-glass search-box-icon"></span>
|
||||
|
||||
</form>
|
||||
<div class="position-absolute top-50 start-50 translate-middle ">
|
||||
{% if request.GET.q %}
|
||||
<a href="{% url request.resolver_match.view_name %}" class="btn p-0"><span class="fas fa-times text-danger "></span></a>
|
||||
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -11,7 +11,7 @@
|
||||
<table class="table fs-9 mb-0 border-top border-translucent">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="sort white-space-nowrap align-middle" scope="col">{% trans "Estimate Number" %}</th>
|
||||
<th class="sort white-space-nowrap align-middle" scope="col">{% trans "Quotation 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 "Status" %}</th>
|
||||
<th class="sort white-space-nowrap align-middle" scope="col">{% trans "Status Date" %}</th>
|
||||
@ -23,7 +23,7 @@
|
||||
{% for estimate in estimates %}
|
||||
<tr class="hover-actions-trigger btn-reveal-trigger position-static">
|
||||
<td class="align-middle product white-space-nowrap py-0">{{ estimate.estimate_number }}</td>
|
||||
<td class="align-middle product white-space-nowrap">{{ estimate.customer }}</td>
|
||||
<td class="align-middle product white-space-nowrap">{{ estimate.customer.customer_name }}</td>
|
||||
<td class="align-middle product white-space-nowrap">
|
||||
{% if estimate.status == 'draft' %}
|
||||
<span class="badge badge-phoenix badge-phoenix-warning">{% trans "Draft" %}</span>
|
||||
@ -49,6 +49,10 @@
|
||||
<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>
|
||||
</td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
|
||||
@ -52,24 +52,7 @@
|
||||
/>
|
||||
<span class="fas fa-search search-box-icon"></span>
|
||||
</form>
|
||||
</div><a class="btn btn-phoenix-primary px-3 me-1 border-0 text-body" href="#" data-bs-toggle="tooltip" data-bs-placement="top" data-bs-title="List view"><span class="fa-solid fa-list fs-10"></span></a><a class="btn btn-phoenix-primary px-3 me-1" href="#" data-bs-toggle="tooltip" data-bs-placement="top" data-bs-title="Board view">
|
||||
<svg width="9" height="9" viewbox="0 0 9 9" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M0 0.5C0 0.223857 0.223858 0 0.5 0H1.83333C2.10948 0 2.33333 0.223858 2.33333 0.5V1.83333C2.33333 2.10948 2.10948 2.33333 1.83333 2.33333H0.5C0.223857 2.33333 0 2.10948 0 1.83333V0.5Z" fill="currentColor"></path>
|
||||
<path d="M3.33333 0.5C3.33333 0.223857 3.55719 0 3.83333 0H5.16667C5.44281 0 5.66667 0.223858 5.66667 0.5V1.83333C5.66667 2.10948 5.44281 2.33333 5.16667 2.33333H3.83333C3.55719 2.33333 3.33333 2.10948 3.33333 1.83333V0.5Z" fill="currentColor"></path>
|
||||
<path d="M6.66667 0.5C6.66667 0.223857 6.89052 0 7.16667 0H8.5C8.77614 0 9 0.223858 9 0.5V1.83333C9 2.10948 8.77614 2.33333 8.5 2.33333H7.16667C6.89052 2.33333 6.66667 2.10948 6.66667 1.83333V0.5Z" fill="currentColor"></path>
|
||||
<path d="M0 3.83333C0 3.55719 0.223858 3.33333 0.5 3.33333H1.83333C2.10948 3.33333 2.33333 3.55719 2.33333 3.83333V5.16667C2.33333 5.44281 2.10948 5.66667 1.83333 5.66667H0.5C0.223857 5.66667 0 5.44281 0 5.16667V3.83333Z" fill="currentColor"></path>
|
||||
<path d="M3.33333 3.83333C3.33333 3.55719 3.55719 3.33333 3.83333 3.33333H5.16667C5.44281 3.33333 5.66667 3.55719 5.66667 3.83333V5.16667C5.66667 5.44281 5.44281 5.66667 5.16667 5.66667H3.83333C3.55719 5.66667 3.33333 5.44281 3.33333 5.16667V3.83333Z" fill="currentColor"></path>
|
||||
<path d="M6.66667 3.83333C6.66667 3.55719 6.89052 3.33333 7.16667 3.33333H8.5C8.77614 3.33333 9 3.55719 9 3.83333V5.16667C9 5.44281 8.77614 5.66667 8.5 5.66667H7.16667C6.89052 5.66667 6.66667 5.44281 6.66667 5.16667V3.83333Z" fill="currentColor"></path>
|
||||
<path d="M0 7.16667C0 6.89052 0.223858 6.66667 0.5 6.66667H1.83333C2.10948 6.66667 2.33333 6.89052 2.33333 7.16667V8.5C2.33333 8.77614 2.10948 9 1.83333 9H0.5C0.223857 9 0 8.77614 0 8.5V7.16667Z" fill="currentColor"></path>
|
||||
<path d="M3.33333 7.16667C3.33333 6.89052 3.55719 6.66667 3.83333 6.66667H5.16667C5.44281 6.66667 5.66667 6.89052 5.66667 7.16667V8.5C5.66667 8.77614 5.44281 9 5.16667 9H3.83333C3.55719 9 3.33333 8.77614 3.33333 8.5V7.16667Z" fill="currentColor"></path>
|
||||
<path d="M6.66667 7.16667C6.66667 6.89052 6.89052 6.66667 7.16667 6.66667H8.5C8.77614 6.66667 9 6.89052 9 7.16667V8.5C9 8.77614 8.77614 9 8.5 9H7.16667C6.89052 9 6.66667 8.77614 6.66667 8.5V7.16667Z" fill="currentColor"></path>
|
||||
</svg></a><a class="btn btn-phoenix-primary px-3" href="#" data-bs-toggle="tooltip" data-bs-placement="top" data-bs-title="Card view">
|
||||
<svg width="9" height="9" viewBox="0 0 9 9" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M0 0.5C0 0.223858 0.223858 0 0.5 0H3.5C3.77614 0 4 0.223858 4 0.5V3.5C4 3.77614 3.77614 4 3.5 4H0.5C0.223858 4 0 3.77614 0 3.5V0.5Z" fill="currentColor"></path>
|
||||
<path d="M0 5.5C0 5.22386 0.223858 5 0.5 5H3.5C3.77614 5 4 5.22386 4 5.5V8.5C4 8.77614 3.77614 9 3.5 9H0.5C0.223858 9 0 8.77614 0 8.5V5.5Z" fill="currentColor"></path>
|
||||
<path d="M5 0.5C5 0.223858 5.22386 0 5.5 0H8.5C8.77614 0 9 0.223858 9 0.5V3.5C9 3.77614 8.77614 4 8.5 4H5.5C5.22386 4 5 3.77614 5 3.5V0.5Z" fill="currentColor"></path>
|
||||
<path d="M5 5.5C5 5.22386 5.22386 5 5.5 5H8.5C8.77614 5 9 5.22386 9 5.5V8.5C9 8.77614 8.77614 9 8.5 9H5.5C5.22386 9 5 8.77614 5 8.5V5.5Z" fill="currentColor"></path>
|
||||
</svg></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -109,24 +92,16 @@
|
||||
hx-on::before-request="filter_before_request()"
|
||||
hx-on::after-request="filter_after_request()">Search</button>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="table-responsive scrollbar transition">
|
||||
<div class="d-flex flex-wrap align-items-center justify-content-between py-3 pe-0 fs-9 border-bottom border-translucent">
|
||||
<div class="d-flex">
|
||||
</div>
|
||||
<div class="d-flex flex-wrap align-items-center justify-content-between py-3 pe-0 fs-9">
|
||||
|
||||
<div class="d-flex" hx-boost="true" hx-push-url='false' hx-include=".make,.model,.year,.car_status" hx-target=".table-responsive" hx-select=".table-responsive" hx-swap="innerHTML show:window:top" hx-indicator=".htmx-indicator"
|
||||
hx-on::before-request="on_before_request()"
|
||||
hx-on::after-request="on_after_request()">
|
||||
{% if page_obj.has_previous %}
|
||||
<a href="{% url 'car_list' %}?page={{page_obj.previous_page_number}}" class="page-link" data-list-pagination="prev"><span class="fas fa-chevron-left"></span></a>
|
||||
{% endif %}
|
||||
<ul class="mb-0 pagination">Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}</ul>
|
||||
{% if page_obj.has_next %}
|
||||
<a href="{% url 'car_list' %}?page={{page_obj.next_page_number}}" class="page-link pe-0" data-list-pagination="next"><span class="fas fa-chevron-right"></span></a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<table class="table fs-9 mb-0 border-top border-translucent">
|
||||
|
||||
<table class="table table-sm fs-9 mb-0 border-translucent">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="sort white-space-nowrap align-middle ps-0" scope="col" data-sort="projectName" style="width:10%;">
|
||||
@ -147,29 +122,29 @@
|
||||
<tbody class="list" id="project-list-table-body">
|
||||
{% for tx in txs %}
|
||||
<tr class="position-static">
|
||||
<td class="align-middle white-space-nowrap deadline ps-3 py-4">
|
||||
<td class="align-middle white-space-nowrap deadline">
|
||||
<p class="mb-0 fs-9 text-body">{{tx.customer.customer_name}}</p>
|
||||
</td>
|
||||
<td class="align-middle white-space-nowrap deadline ps-3 py-4">
|
||||
<td class="align-middle white-space-nowrap deadline">
|
||||
<p class="mb-0 fs-9 text-body">{{tx.customer.address_1}}</p>
|
||||
</td>
|
||||
<td class="align-middle white-space-nowrap deadline ps-3 py-4">
|
||||
<td class="align-middle white-space-nowrap deadline">
|
||||
<p class="mb-0 fs-9 text-body">{{tx.customer.phone}}</p>
|
||||
</td>
|
||||
<td class="align-middle time white-space-nowrap ps-0 projectName py-4">{{tx.info.make}}</td>
|
||||
<td class="align-middle white-space-nowrap start ps-3 py-4">
|
||||
<td class="align-middle time white-space-nowrap projectName">{{tx.info.make}}</td>
|
||||
<td class="align-middle white-space-nowrap start">
|
||||
<p class="mb-0 fs-9 text-body">{{tx.info.model}}</p>
|
||||
</td>
|
||||
<td class="align-middle white-space-nowrap deadline ps-3 py-4">
|
||||
<td class="align-middle white-space-nowrap deadline">
|
||||
<p class="mb-0 fs-9 text-body">{{tx.info.vin}}</p>
|
||||
</td>
|
||||
<td class="align-middle white-space-nowrap task ps-3 py-4">
|
||||
<td class="align-middle white-space-nowrap task">
|
||||
<p class="fw-bo text-body fs-9 mb-0">{{tx.info.trim}}</p>
|
||||
</td>
|
||||
<td class="align-middle white-space-nowrap task ps-3 py-4">
|
||||
<td class="align-middle white-space-nowrap task">
|
||||
<p class="fw-bo text-body fs-9 mb-0">{{tx.finance.total}}</p>
|
||||
</td>
|
||||
<td class="align-middle white-space-nowrap task ps-3 py-4">
|
||||
<td class="align-middle white-space-nowrap task">
|
||||
{% if tx.has_estimate %}
|
||||
<p class="fw-bo text-body fs-9 mb-0">
|
||||
<a href="{% url 'estimate_detail' tx.estimate.uuid %}">
|
||||
@ -187,7 +162,7 @@
|
||||
</p>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="align-middle white-space-nowrap task ps-3 py-4">
|
||||
<td class="align-middle white-space-nowrap task">
|
||||
{% if tx.has_invoice %}
|
||||
<p class="fw-bo text-body fs-9 mb-0">
|
||||
<a href="{% url 'invoice_detail' tx.invoice.uuid %}">
|
||||
@ -235,18 +210,15 @@
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div class="d-flex flex-wrap align-items-center justify-content-between py-3 pe-0 fs-9 border-bottom border-translucent">
|
||||
<div class="d-flex">
|
||||
{% if is_paginated %}
|
||||
{% include 'partials/pagination.html' %}
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block customJS %}
|
||||
|
||||
39
test.json
Normal file
@ -0,0 +1,39 @@
|
||||
|
||||
{
|
||||
"type": "customer",
|
||||
"customer_info":
|
||||
{
|
||||
"first_name": "firstNameT",
|
||||
"first_name_arabic": "firstName",
|
||||
"middle_name": "fatherNameT",
|
||||
"middle_name_arabic": "fatherName",
|
||||
"last_name": "familyNameT",
|
||||
"last_name_arabic": "familyName",
|
||||
"national_id": "id",
|
||||
"birth_date": "birthDate",
|
||||
"nationality": "nationalityDescEn"
|
||||
},
|
||||
|
||||
"elm":
|
||||
{
|
||||
"birthDate": "1985-12-20",
|
||||
"familyName": "العنزي",
|
||||
"familyNameT": "Alenazi",
|
||||
"fatherName": "الراوي",
|
||||
"fatherNameT": "Alrawi",
|
||||
"firstName": "هليل",
|
||||
"firstNameT": "Hilayel",
|
||||
"grandFatherName": "عثمان",
|
||||
"grandFatherNameT": "Othman",
|
||||
"hasIdExpired": 2,
|
||||
"id": 1020060808,
|
||||
"idExpirationDate": "2029-01-15",
|
||||
"nationalityCode": 100,
|
||||
"nationalityDescAr": "سعودي",
|
||||
"nationalityDescEn": "Saudi",
|
||||
"sexCode": 1,
|
||||
"sexDescAr": "ذكر",
|
||||
"sexDescEn": "Male"
|
||||
}
|
||||
|
||||
}
|
||||