update
This commit is contained in:
parent
dbf4c6f91d
commit
692fa3f0ed
1
.idea/car_inventory.iml
generated
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
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>
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -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)
|
||||
@ -43,6 +44,7 @@ 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"
|
||||
@ -115,3 +117,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]
|
||||
@ -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__)
|
||||
@ -273,6 +277,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)
|
||||
@ -281,6 +286,8 @@ class HomeView(TemplateView):
|
||||
context = super().get_context_data(**kwargs)
|
||||
dealer = get_user_type(self.request)
|
||||
|
||||
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()
|
||||
@ -289,24 +296,44 @@ class HomeView(TemplateView):
|
||||
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["total_cost_price"] or 0
|
||||
total_selling_price = stats["total_selling_price"] or 0
|
||||
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
|
||||
|
||||
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
|
||||
# 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,
|
||||
})
|
||||
|
||||
return context
|
||||
|
||||
class TestView(TemplateView):
|
||||
template_name = "test.html"
|
||||
@ -663,7 +690,8 @@ class CarListView(LoginRequiredMixin, ListView):
|
||||
paginate_by = 10
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
cars = models.Car.objects.all()
|
||||
dealer = get_user_type(self.request)
|
||||
cars = models.Car.objects.filter(dealer=dealer).order_by("receiving_date")
|
||||
|
||||
context["stats"] = {
|
||||
'all': cars.count(),
|
||||
@ -672,16 +700,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))
|
||||
@ -1205,9 +1232,7 @@ 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(
|
||||
@ -1312,6 +1337,7 @@ def CustomerCreateView(request):
|
||||
"address_1": form.cleaned_data["address"],
|
||||
"phone": form.cleaned_data["phone_number"],
|
||||
"email": email,
|
||||
|
||||
}
|
||||
)
|
||||
|
||||
@ -2406,10 +2432,17 @@ 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, 20)
|
||||
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):
|
||||
@ -2428,6 +2461,10 @@ class EstimateListView(LoginRequiredMixin, ListView):
|
||||
return queryset
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# class EstimateCreateView(LoginRequiredMixin, SuccessMessageMixin, CreateView):
|
||||
# model = EstimateModel
|
||||
# form_class = EstimateModelCreateForm
|
||||
@ -3106,7 +3143,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 +4355,9 @@ 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):
|
||||
dealer = get_user_type(self.request)
|
||||
staff = self.request.user.staff
|
||||
return Appointment.objects.filter(appointment_request__staff_member=staff).all()
|
||||
|
||||
|
||||
|
||||
89
ofx_sample.ofx
Normal file
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/DejaVuSans.ttf
Normal file
Binary file not shown.
BIN
static/assets/fonts/MarkaziText%5Bwght%5D.ttf
Normal file
BIN
static/assets/fonts/MarkaziText%5Bwght%5D.ttf
Normal file
Binary file not shown.
BIN
static/assets/fonts/Roboto%5Bwdth,wght%5D.ttf
Normal file
BIN
static/assets/fonts/Roboto%5Bwdth,wght%5D.ttf
Normal file
Binary file not shown.
BIN
static/images/export_pdf/logos/logo-d-pdf.png
Normal file
BIN
static/images/export_pdf/logos/logo-d-pdf.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
BIN
static/images/logos/users/Icon_Black4x_B0iEnf0.png
Normal file
BIN
static/images/logos/users/Icon_Black4x_B0iEnf0.png
Normal file
Binary file not shown.
|
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' %}
|
||||
|
||||
@ -105,12 +105,10 @@
|
||||
</table>
|
||||
</div>
|
||||
<div class="row align-items-center justify-content-end py-4 pe-0 fs-9">
|
||||
|
||||
<!-- Optional: Pagination -->
|
||||
{% if is_paginated %}
|
||||
{% include 'partials/pagination.html' %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% include 'modal/delete_modal.html' %}
|
||||
|
||||
|
||||
@ -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,6 +73,12 @@
|
||||
</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>
|
||||
@ -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,7 +38,7 @@
|
||||
<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>{{ _("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">
|
||||
@ -52,38 +52,20 @@
|
||||
/>
|
||||
<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>
|
||||
<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()"
|
||||
>
|
||||
hx-on::after-request="filter_after_request()">
|
||||
<option selected="" value="" disabled>Select Make</option>
|
||||
{% for m in make %}
|
||||
<option value="{{ m.pk }}">{{ 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()"
|
||||
>
|
||||
@ -92,24 +74,24 @@
|
||||
<option value="{{ m.pk }}">{{ m.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<select class="form-select year" name="year" aria-label="Default select example">
|
||||
<select class="form-select form-control-sm me-1 year" name="year" aria-label="Default select example">
|
||||
<option selected="" value="" disabled>Select 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">
|
||||
<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">
|
||||
@ -117,65 +99,62 @@
|
||||
<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-top 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">
|
||||
<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}}</a></td>
|
||||
<td class="align-middle white-space-nowrap start">
|
||||
<p class="mb-0 fs-9 text-body">{{car.id_car_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">{{car.year}}</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.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,24 +162,21 @@
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<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>
|
||||
</div>
|
||||
<div class="row d-flex justify-content-end align-items-center p-3">
|
||||
{% if is_paginated %}
|
||||
{% include 'partials/pagination.html' %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@ -226,6 +202,8 @@
|
||||
}
|
||||
function toggle_filter(){
|
||||
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)
|
||||
|
||||
@ -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 %}
|
||||
|
||||
@ -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>
|
||||
@ -48,6 +48,10 @@
|
||||
class="btn btn-sm btn-phoenix-success">
|
||||
{% 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 %}
|
||||
|
||||
@ -118,11 +118,11 @@
|
||||
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>
|
||||
<a href="{% url 'sales_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>
|
||||
<a href="{% url 'sales_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>
|
||||
|
||||
39
test.json
Normal file
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"
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user