update and formatting

This commit is contained in:
ismail 2025-06-25 14:29:38 +03:00
parent 3ce318317e
commit a930acfae5
43 changed files with 859 additions and 712 deletions

1
.gitignore vendored
View File

@ -43,6 +43,7 @@ Makefile
.idea/**/dynamic.xml .idea/**/dynamic.xml
.idea/**/uiDesigner.xml .idea/**/uiDesigner.xml
.idea/**/dbnavigator.xml .idea/**/dbnavigator.xml
**/migrations/
# Gradle # Gradle
.idea/**/gradle.xml .idea/**/gradle.xml

View File

@ -124,10 +124,8 @@ class DealerSlugMiddleware:
def process_view(self, request, view_func, view_args, view_kwargs): def process_view(self, request, view_func, view_args, view_kwargs):
if request.user.is_authenticated: if request.user.is_authenticated:
dealer = get_user_type(request) dealer = get_user_type(request)
if 'dealer_slug' not in view_kwargs: if "dealer_slug" not in view_kwargs:
return redirect('home', return redirect("home", dealer_slug=dealer.slug, **view_kwargs)
dealer_slug=dealer.slug, elif view_kwargs["dealer_slug"] != dealer.slug:
**view_kwargs)
elif view_kwargs['dealer_slug'] != dealer.slug:
raise Http404("Dealer slug mismatch") raise Http404("Dealer slug mismatch")
return None return None

View File

@ -1,19 +0,0 @@
# Generated by Django 5.2.1 on 2025-06-23 12:21
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('inventory', '0010_alter_saleorder_created_by_and_more'),
]
operations = [
migrations.AddField(
model_name='dealer',
name='business_name',
field=models.CharField(default='poiiiioioioi', max_length=255, verbose_name='Buiseness Name'),
preserve_default=False,
),
]

View File

@ -1,17 +0,0 @@
# Generated by Django 5.2.1 on 2025-06-24 12:55
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('inventory', '0011_dealer_business_name'),
]
operations = [
migrations.RemoveField(
model_name='dealer',
name='business_name',
),
]

View File

@ -69,10 +69,8 @@ class LocalizedNameMixin:
class DealerSlugMixin: class DealerSlugMixin:
def dispatch(self, request, *args, **kwargs): def dispatch(self, request, *args, **kwargs):
if request.user.is_authenticated: if request.user.is_authenticated:
if 'dealer_slug' not in kwargs: if "dealer_slug" not in kwargs:
return redirect('home', return redirect("home", dealer_slug=request.dealer.slug, **kwargs)
dealer_slug=request.dealer.slug, elif kwargs["dealer_slug"] != request.dealer.slug:
**kwargs)
elif kwargs['dealer_slug'] != request.dealer.slug:
raise Http404("Dealer slug mismatch") raise Http404("Dealer slug mismatch")
return super().dispatch(request, *args, **kwargs) return super().dispatch(request, *args, **kwargs)

View File

@ -2482,16 +2482,22 @@ class SaleOrder(models.Model):
item_data = self.invoice.get_itemtxs_data() item_data = self.invoice.get_itemtxs_data()
if item_data: if item_data:
return item_data return item_data
return [] # Return an empty list if no invoice or no item data return [] # Return an empty list if no invoice or no item data
@property @property
def cars(self): def cars(self):
# Check if self.items is not empty before trying to iterate # Check if self.items is not empty before trying to iterate
if self.items.exists() if hasattr(self.items, 'exists') else self.items: # Handle both QuerySet and list if (
self.items.exists() if hasattr(self.items, "exists") else self.items
): # Handle both QuerySet and list
# Ensure x is an *instance* of ItemTransactionModel # Ensure x is an *instance* of ItemTransactionModel
# item_model should be a ForeignKey to your CarModel within ItemTransactionModel # item_model should be a ForeignKey to your CarModel within ItemTransactionModel
return [x.item_model.car for x in self.items if hasattr(x, 'item_model') and hasattr(x.item_model, 'car')] return [
return [] # Return an empty list if no items or no associated cars x.item_model.car
for x in self.items
if hasattr(x, "item_model") and hasattr(x.item_model, "car")
]
return [] # Return an empty list if no items or no associated cars
class CustomGroup(models.Model): class CustomGroup(models.Model):

View File

@ -1,4 +1,3 @@
from typing import Union from typing import Union
from random import randint from random import randint
from django import template from django import template
@ -493,15 +492,15 @@ def transactions_table(
qs = object_type.get_transaction_queryset(annotated=True) qs = object_type.get_transaction_queryset(annotated=True)
transaction_model_qs = qs.annotate( transaction_model_qs = qs.annotate(
debit_credit_sort_order=Case( debit_credit_sort_order=Case(
When(tx_type='debit', then=Value(0)), # Debits will get sort order 0 When(tx_type="debit", then=Value(0)), # Debits will get sort order 0
When(tx_type='credit', then=Value(1)), # Credits will get sort order 1 When(tx_type="credit", then=Value(1)), # Credits will get sort order 1
default=Value(2), # Fallback for any other tx_type, if applicable default=Value(2), # Fallback for any other tx_type, if applicable
output_field=IntegerField() output_field=IntegerField(),
) )
).order_by( ).order_by(
'-timestamp', # Primary sort: chronological (oldest first) "-timestamp", # Primary sort: chronological (oldest first)
'-debit_credit_sort_order', # Secondary sort: Debits (0) before Credits (1) "-debit_credit_sort_order", # Secondary sort: Debits (0) before Credits (1)
'pk' # Optional: Tie-breaker for consistent order "pk", # Optional: Tie-breaker for consistent order
) )
elif isinstance(object_type, InvoiceModel): elif isinstance(object_type, InvoiceModel):
transaction_model_qs = object_type.get_transaction_queryset( transaction_model_qs = object_type.get_transaction_queryset(

View File

@ -280,20 +280,48 @@ urlpatterns = [
), ),
# Car URLs # Car URLs
path("<slug:dealer_slug>/cars/upload_cars/", views.upload_cars, name="upload_cars"), path("<slug:dealer_slug>/cars/upload_cars/", views.upload_cars, name="upload_cars"),
path("<slug:dealer_slug>/cars/<uuid:pk>/upload_cars/", views.upload_cars, name="upload_cars"), path(
"<slug:dealer_slug>/cars/<uuid:pk>/upload_cars/",
views.upload_cars,
name="upload_cars",
),
path("<slug:dealer_slug>/cars/add/", views.CarCreateView.as_view(), name="car_add"), path("<slug:dealer_slug>/cars/add/", views.CarCreateView.as_view(), name="car_add"),
path("<slug:dealer_slug>/cars/inventory/", views.CarInventory.as_view(), name="car_inventory_all"), path(
"<slug:dealer_slug>/cars/inventory/",
views.CarInventory.as_view(),
name="car_inventory_all",
),
path( path(
"<slug:dealer_slug>/cars/inventory/<slug:make_id>/<slug:model_id>/<slug:trim_id>/", "<slug:dealer_slug>/cars/inventory/<slug:make_id>/<slug:model_id>/<slug:trim_id>/",
views.CarInventory.as_view(), views.CarInventory.as_view(),
name="car_inventory", name="car_inventory",
), ),
path("<slug:dealer_slug>/cars/inventory/stats", views.inventory_stats_view, name="inventory_stats"), path(
path("<slug:dealer_slug>/cars/inventory/list", views.CarListView.as_view(), name="car_list"), "<slug:dealer_slug>/cars/inventory/stats",
path("<slug:dealer_slug>/cars/<slug:slug>/", views.CarDetailView.as_view(), name="car_detail"), views.inventory_stats_view,
name="inventory_stats",
),
path(
"<slug:dealer_slug>/cars/inventory/list",
views.CarListView.as_view(),
name="car_list",
),
path(
"<slug:dealer_slug>/cars/<slug:slug>/",
views.CarDetailView.as_view(),
name="car_detail",
),
path("cars/<slug:slug>/history/", views.car_history, name="car_history"), path("cars/<slug:slug>/history/", views.car_history, name="car_history"),
path("<slug:dealer_slug>/cars/<slug:slug>/update/", views.CarUpdateView.as_view(), name="car_update"), path(
path("<slug:dealer_slug>/cars/<slug:slug>/delete/", views.CarDeleteView.as_view(), name="car_delete"), "<slug:dealer_slug>/cars/<slug:slug>/update/",
views.CarUpdateView.as_view(),
name="car_update",
),
path(
"<slug:dealer_slug>/cars/<slug:slug>/delete/",
views.CarDeleteView.as_view(),
name="car_delete",
),
path( path(
"<slug:dealer_slug>/cars/<slug:slug>/finance/create/", "<slug:dealer_slug>/cars/<slug:slug>/finance/create/",
views.CarFinanceCreateView.as_view(), views.CarFinanceCreateView.as_view(),
@ -309,9 +337,13 @@ urlpatterns = [
views.bulk_update_car_price, views.bulk_update_car_price,
name="bulk_update_car_price", name="bulk_update_car_price",
), ),
path("<slug:dealer_slug>/ajax/", views.AjaxHandlerView.as_view(), name="ajax_handler"),
path( path(
"<slug:dealer_slug>/cars/<slug:slug>/add-color/", views.CarColorCreate.as_view(), name="add_color" "<slug:dealer_slug>/ajax/", views.AjaxHandlerView.as_view(), name="ajax_handler"
),
path(
"<slug:dealer_slug>/cars/<slug:slug>/add-color/",
views.CarColorCreate.as_view(),
name="add_color",
), ),
path( path(
"<slug:dealer_slug>/car/colors/<slug:slug>/update/", "<slug:dealer_slug>/car/colors/<slug:slug>/update/",
@ -355,7 +387,11 @@ urlpatterns = [
), ),
path("cars/inventory/search/", views.SearchCodeView.as_view(), name="car_search"), path("cars/inventory/search/", views.SearchCodeView.as_view(), name="car_search"),
# path('cars/<int:car_pk>/colors/<int:pk>/update/',views.CarColorUpdateView.as_view(),name='color_update'), # path('cars/<int:car_pk>/colors/<int:pk>/update/',views.CarColorUpdateView.as_view(),name='color_update'),
path("<slug:dealer_slug>/cars/reserve/<slug:slug>/", views.reserve_car_view, name="reserve_car"), path(
"<slug:dealer_slug>/cars/reserve/<slug:slug>/",
views.reserve_car_view,
name="reserve_car",
),
path( path(
"<slug:dealer_slug>/reservations/<int:reservation_id>/", "<slug:dealer_slug>/reservations/<int:reservation_id>/",
views.manage_reservation, views.manage_reservation,
@ -373,17 +409,17 @@ urlpatterns = [
), ),
# sales list # sales list
path( path(
"sales/list/", "<slug:dealer_slug>/sales/list/",
views.sales_list_view, views.sales_list_view,
name="sales_list", name="sales_list",
), ),
path( path(
"sale_orders/<int:pk>/", "<slug:dealer_slug>/sale_orders/<int:pk>/",
views.SaleOrderDetailView.as_view(), views.SaleOrderDetailView.as_view(),
name="order_detail", name="order_detail",
), ),
path( path(
"inventory/<slug:entity_slug>/list/", "<slug:dealer_slug>/inventory/<slug:entity_slug>/list/",
views.InventoryListView.as_view(), views.InventoryListView.as_view(),
name="inventort_list", name="inventort_list",
), ),
@ -614,7 +650,9 @@ urlpatterns = [
), ),
# Bank Account # Bank Account
path( path(
"<slug:dealer_slug>/bank_accounts/", views.BankAccountListView.as_view(), name="bank_account_list" "<slug:dealer_slug>/bank_accounts/",
views.BankAccountListView.as_view(),
name="bank_account_list",
), ),
path( path(
"<slug:dealer_slug>/bank_accounts/<uuid:pk>/", "<slug:dealer_slug>/bank_accounts/<uuid:pk>/",
@ -637,31 +675,49 @@ urlpatterns = [
name="bank_account_delete", name="bank_account_delete",
), ),
# Account # Account
path("<slug:dealer_slug>/coa_accounts/", views.AccountListView.as_view(), name="account_list"), path(
"<slug:dealer_slug>/coa_accounts/",
views.AccountListView.as_view(),
name="account_list",
),
path( path(
"<slug:dealer_slug>/coa_accounts/<uuid:pk>/", "<slug:dealer_slug>/coa_accounts/<uuid:pk>/",
views.AccountDetailView.as_view(), views.AccountDetailView.as_view(),
name="account_detail", name="account_detail",
), ),
path( path(
"<slug:dealer_slug>/coa_accounts/create/", views.AccountCreateView.as_view(), name="account_create" "<slug:dealer_slug>/coa_accounts/create/",
views.AccountCreateView.as_view(),
name="account_create",
), ),
path( path(
"<slug:dealer_slug>/coa_accounts/<uuid:pk>/update/", "<slug:dealer_slug>/coa_accounts/<uuid:pk>/update/",
views.AccountUpdateView.as_view(), views.AccountUpdateView.as_view(),
name="account_update", name="account_update",
), ),
path("<slug:dealer_slug>/coa_accounts/<uuid:pk>/delete/", views.account_delete, name="account_delete"), path(
"<slug:dealer_slug>/coa_accounts/<uuid:pk>/delete/",
views.account_delete,
name="account_delete",
),
################################################# #################################################
# Estimate # Estimate
################################################# #################################################
path("<slug:dealer_slug>/sales/estimates/", views.EstimateListView.as_view(), name="estimate_list"), path(
"<slug:dealer_slug>/sales/estimates/",
views.EstimateListView.as_view(),
name="estimate_list",
),
path( path(
"<slug:dealer_slug>/sales/estimates/<uuid:pk>/", "<slug:dealer_slug>/sales/estimates/<uuid:pk>/",
views.EstimateDetailView.as_view(), views.EstimateDetailView.as_view(),
name="estimate_detail", name="estimate_detail",
), ),
path("<slug:dealer_slug>/sales/estimates/create/", views.create_estimate, name="estimate_create"), path(
"<slug:dealer_slug>/sales/estimates/create/",
views.create_estimate,
name="estimate_create",
),
path( path(
"<slug:dealer_slug>/sales/estimates/create/<slug:slug>/", "<slug:dealer_slug>/sales/estimates/create/<slug:slug>/",
views.create_estimate, views.create_estimate,
@ -683,7 +739,9 @@ urlpatterns = [
name="payment_request", name="payment_request",
), ),
path( path(
"<slug:dealer_slug>/sales/estimates/<uuid:pk>/send_email", views.send_email_view, name="send_email" "<slug:dealer_slug>/sales/estimates/<uuid:pk>/send_email",
views.send_email_view,
name="send_email",
), ),
path( path(
"<slug:dealer_slug>/sales/estimates/<uuid:pk>/sale_order/", "<slug:dealer_slug>/sales/estimates/<uuid:pk>/sale_order/",
@ -703,9 +761,15 @@ urlpatterns = [
############################################### ###############################################
# Invoice # Invoice
############################################### ###############################################
path("<slug:dealer_slug>/sales/invoices/", views.InvoiceListView.as_view(), name="invoice_list"),
path( path(
"<slug:dealer_slug>/sales/invoices/<uuid:pk>/create/", views.invoice_create, name="invoice_create" "<slug:dealer_slug>/sales/invoices/",
views.InvoiceListView.as_view(),
name="invoice_list",
),
path(
"<slug:dealer_slug>/sales/invoices/<uuid:pk>/create/",
views.invoice_create,
name="invoice_create",
), ),
path( path(
"<slug:dealer_slug>/sales/invoices/<uuid:pk>/", "<slug:dealer_slug>/sales/invoices/<uuid:pk>/",
@ -740,7 +804,9 @@ urlpatterns = [
# path('sales/estimates/<uuid:pk>/preview/', views.EstimatePreviewView.as_view(), name='estimate_preview'), # path('sales/estimates/<uuid:pk>/preview/', views.EstimatePreviewView.as_view(), name='estimate_preview'),
# path('send_email/<uuid:pk>', views.send_email, name='send_email'), # path('send_email/<uuid:pk>', views.send_email, name='send_email'),
# Payment # Payment
path("<slug:dealer_slug>/sales/payments/", views.PaymentListView, name="payment_list"), path(
"<slug:dealer_slug>/sales/payments/", views.PaymentListView, name="payment_list"
),
path( path(
"<slug:dealer_slug>/sales/payments/<uuid:pk>/create/", "<slug:dealer_slug>/sales/payments/<uuid:pk>/create/",
views.PaymentCreateView, views.PaymentCreateView,
@ -793,7 +859,11 @@ urlpatterns = [
name="item_expense_update", name="item_expense_update",
), ),
# Bills # Bills
path("<slug:dealer_slug>/items/bills/", views.BillListView.as_view(), name="bill_list"), path(
"<slug:dealer_slug>/items/bills/",
views.BillListView.as_view(),
name="bill_list",
),
# path("items/bills/create/", views.BillModelCreateViewView.as_view(), name="bill_create"), # path("items/bills/create/", views.BillModelCreateViewView.as_view(), name="bill_create"),
path( path(
"<slug:dealer_slug>/items/bills/<slug:entity_slug>/create/", "<slug:dealer_slug>/items/bills/<slug:entity_slug>/create/",
@ -1121,7 +1191,6 @@ urlpatterns = [
views.PurchaseOrderMarkAsVoidView.as_view(), views.PurchaseOrderMarkAsVoidView.as_view(),
name="po-action-mark-as-void", name="po-action-mark-as-void",
), ),
] ]
handler404 = "inventory.views.custom_page_not_found_view" handler404 = "inventory.views.custom_page_not_found_view"

View File

@ -349,7 +349,7 @@ class HomeView(LoginRequiredMixin, TemplateView):
template_name = "index.html" template_name = "index.html"
def dispatch(self, request,*args, **kwargs): def dispatch(self, request, *args, **kwargs):
# Redirect unauthenticated users to the welcome page # Redirect unauthenticated users to the welcome page
if not request.user.is_authenticated: if not request.user.is_authenticated:
return redirect("welcome") return redirect("welcome")
@ -620,8 +620,12 @@ class CarCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView):
def get_success_url(self): def get_success_url(self):
"""Determine the redirect URL based on user choice.""" """Determine the redirect URL based on user choice."""
if self.request.POST.get("add_another"): if self.request.POST.get("add_another"):
return reverse("car_add", kwargs={"dealer_slug": self.kwargs["dealer_slug"]}) return reverse(
return reverse("inventory_stats", kwargs={"dealer_slug": self.kwargs["dealer_slug"]}) "car_add", kwargs={"dealer_slug": self.kwargs["dealer_slug"]}
)
return reverse(
"inventory_stats", kwargs={"dealer_slug": self.kwargs["dealer_slug"]}
)
def form_valid(self, form): def form_valid(self, form):
dealer = get_user_type(self.request) dealer = get_user_type(self.request)
@ -675,7 +679,7 @@ class AjaxHandlerView(LoginRequiredMixin, View):
:ivar request: Django request object containing HTTP request details. :ivar request: Django request object containing HTTP request details.
""" """
def get(self, request,dealer_slug, *args, **kwargs): def get(self, request, dealer_slug, *args, **kwargs):
action = request.GET.get("action") action = request.GET.get("action")
handlers = { handlers = {
"decode_vin": self.decode_vin, "decode_vin": self.decode_vin,
@ -988,7 +992,13 @@ class CarColorCreate(LoginRequiredMixin, PermissionRequiredMixin, CreateView):
return super().form_valid(form) return super().form_valid(form)
def get_success_url(self): def get_success_url(self):
return reverse_lazy("car_detail", kwargs={"dealer_slug":self.request.dealer.slug,"slug": self.kwargs["slug"]}) return reverse_lazy(
"car_detail",
kwargs={
"dealer_slug": self.request.dealer.slug,
"slug": self.kwargs["slug"],
},
)
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
@ -1027,7 +1037,13 @@ class CarColorsUpdateView(
""" """
# self.object refers to the CarColors instance that was just updated. # self.object refers to the CarColors instance that was just updated.
# self.object.car then refers to the associated Car instance. # self.object.car then refers to the associated Car instance.
return reverse("car_detail", kwargs={"dealer_slug":self.request.dealer.slug,"slug": self.object.car.slug}) return reverse(
"car_detail",
kwargs={
"dealer_slug": self.request.dealer.slug,
"slug": self.object.car.slug,
},
)
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
""" """
@ -1140,7 +1156,7 @@ class CarListView(LoginRequiredMixin, PermissionRequiredMixin, ListView):
@login_required @login_required
def inventory_stats_view(request,dealer_slug): def inventory_stats_view(request, dealer_slug):
""" """
Handle the inventory stats view for a dealer, displaying detailed information Handle the inventory stats view for a dealer, displaying detailed information
about the cars, including counts grouped by make, model, and trim. about the cars, including counts grouped by make, model, and trim.
@ -1245,7 +1261,6 @@ def inventory_stats_view(request,dealer_slug):
return render(request, "inventory/inventory_stats.html", {"inventory": result}) return render(request, "inventory/inventory_stats.html", {"inventory": result})
# @login_required # @login_required
# def inventory_stats_view(request): # def inventory_stats_view(request):
# """ # """
@ -1362,7 +1377,6 @@ def inventory_stats_view(request,dealer_slug):
# return render(request, "inventory/inventory_stats.html", context) # return render(request, "inventory/inventory_stats.html", context)
class CarDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailView): class CarDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailView):
""" """
Provides a detailed view of a single car instance. Provides a detailed view of a single car instance.
@ -1427,7 +1441,10 @@ class CarFinanceCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateVi
return super().form_valid(form) return super().form_valid(form)
def get_success_url(self): def get_success_url(self):
return reverse("car_detail", kwargs={"dealer_slug":self.request.dealer.slug,"slug": self.car.slug}) return reverse(
"car_detail",
kwargs={"dealer_slug": self.request.dealer.slug, "slug": self.car.slug},
)
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
@ -1474,7 +1491,13 @@ class CarFinanceUpdateView(
permission_required = ["inventory.change_carfinance"] permission_required = ["inventory.change_carfinance"]
def get_success_url(self): def get_success_url(self):
return reverse("car_detail", kwargs={"dealer_slug":self.request.dealer.slug,"slug": self.object.car.slug}) return reverse(
"car_detail",
kwargs={
"dealer_slug": self.request.dealer.slug,
"slug": self.object.car.slug,
},
)
def get_form_kwargs(self): def get_form_kwargs(self):
kwargs = super().get_form_kwargs() kwargs = super().get_form_kwargs()
@ -1871,7 +1894,13 @@ class CustomCardCreateView(LoginRequiredMixin, CreateView):
def get_success_url(self): def get_success_url(self):
messages.success(self.request, _("Custom Card added successfully")) messages.success(self.request, _("Custom Card added successfully"))
return reverse_lazy("car_detail", kwargs={"dealer_slug":self.request.dealer.slug,"slug": self.kwargs["slug"]}) return reverse_lazy(
"car_detail",
kwargs={
"dealer_slug": self.request.dealer.slug,
"slug": self.kwargs["slug"],
},
)
class CarRegistrationCreateView(LoginRequiredMixin, CreateView): class CarRegistrationCreateView(LoginRequiredMixin, CreateView):
@ -1913,11 +1942,17 @@ class CarRegistrationCreateView(LoginRequiredMixin, CreateView):
def get_success_url(self): def get_success_url(self):
messages.success(self.request, _("Registration added successfully")) messages.success(self.request, _("Registration added successfully"))
return reverse_lazy("car_detail", kwargs={"dealer_slug":self.request.dealer.slug,"slug": self.kwargs["slug"]}) return reverse_lazy(
"car_detail",
kwargs={
"dealer_slug": self.request.dealer.slug,
"slug": self.kwargs["slug"],
},
)
@login_required() @login_required()
def reserve_car_view(request, dealer_slug,slug): def reserve_car_view(request, dealer_slug, slug):
""" """
Handles car reservation requests. This view requires the user to be logged in Handles car reservation requests. This view requires the user to be logged in
and processes only POST requests. When invoked, it checks if the specified car and processes only POST requests. When invoked, it checks if the specified car
@ -1945,7 +1980,7 @@ def reserve_car_view(request, dealer_slug,slug):
@login_required @login_required
def manage_reservation(request,dealer_slug, reservation_id): def manage_reservation(request, dealer_slug, reservation_id):
""" """
Handles the management of a car reservation, providing options to renew or Handles the management of a car reservation, providing options to renew or
cancel an existing reservation associated with the logged-in user. cancel an existing reservation associated with the logged-in user.
@ -1974,7 +2009,9 @@ def manage_reservation(request,dealer_slug, reservation_id):
reservation.reserved_until = timezone.now() + timezone.timedelta(hours=24) reservation.reserved_until = timezone.now() + timezone.timedelta(hours=24)
reservation.save() reservation.save()
messages.success(request, _("Reservation renewed successfully")) messages.success(request, _("Reservation renewed successfully"))
return redirect("car_detail",dealer_slug=request.dealer.slug, slug=reservation.car.slug) return redirect(
"car_detail", dealer_slug=request.dealer.slug, slug=reservation.car.slug
)
elif action == "cancel": elif action == "cancel":
car = reservation.car car = reservation.car
@ -1982,7 +2019,9 @@ def manage_reservation(request,dealer_slug, reservation_id):
car.status = models.CarStatusChoices.AVAILABLE car.status = models.CarStatusChoices.AVAILABLE
car.save() car.save()
messages.success(request, _("Reservation canceled successfully")) messages.success(request, _("Reservation canceled successfully"))
return redirect("car_detail",dealer_slug=request.dealer.slug, slug=reservation.car.slug) return redirect(
"car_detail", dealer_slug=request.dealer.slug, slug=reservation.car.slug
)
else: else:
return JsonResponse( return JsonResponse(
@ -3454,10 +3493,16 @@ class BankAccountUpdateView(
kwargs["entity_slug"] = entity.slug # Get entity_slug from URL kwargs["entity_slug"] = entity.slug # Get entity_slug from URL
kwargs["user_model"] = entity.admin # Get user_model from the request kwargs["user_model"] = entity.admin # Get user_model from the request
return kwargs return kwargs
def get_success_url(self): def get_success_url(self):
return reverse_lazy("bank_account_detail", kwargs={"dealer_slug": self.kwargs["dealer_slug"], "pk": self.object.pk}) return reverse_lazy(
"bank_account_detail",
kwargs={"dealer_slug": self.kwargs["dealer_slug"], "pk": self.object.pk},
)
@login_required @login_required
def bank_account_delete(request,dealer_slug, pk): def bank_account_delete(request, dealer_slug, pk):
""" """
Delete a bank account entry from the database. Delete a bank account entry from the database.
@ -3590,8 +3635,11 @@ class AccountCreateView(
entity = get_user_type(self.request).entity entity = get_user_type(self.request).entity
form.initial["coa_model"] = entity.get_default_coa() form.initial["coa_model"] = entity.get_default_coa()
return form return form
def get_success_url(self): def get_success_url(self):
return reverse("account_list", kwargs={"dealer_slug":self.kwargs["dealer_slug"]}) return reverse(
"account_list", kwargs={"dealer_slug": self.kwargs["dealer_slug"]}
)
class AccountDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailView): class AccountDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailView):
@ -3691,12 +3739,16 @@ class AccountUpdateView(
form.fields["_ref_node_id"].widget = HiddenInput() form.fields["_ref_node_id"].widget = HiddenInput()
form.fields["_position"].widget = HiddenInput() form.fields["_position"].widget = HiddenInput()
return form return form
def get_success_url(self): def get_success_url(self):
return reverse_lazy("account_list", kwargs={"dealer_slug":self.kwargs["dealer_slug"]}) return reverse_lazy(
"account_list", kwargs={"dealer_slug": self.kwargs["dealer_slug"]}
)
@login_required @login_required
@permission_required("inventory.view_carfinance") @permission_required("inventory.view_carfinance")
def account_delete(request, dealer_slug,pk): def account_delete(request, dealer_slug, pk):
""" """
Handles the deletion of an account object identified by its primary key (pk). Ensures Handles the deletion of an account object identified by its primary key (pk). Ensures
that the user has the necessary permissions to perform the deletion. Successfully that the user has the necessary permissions to perform the deletion. Successfully
@ -3715,13 +3767,13 @@ def account_delete(request, dealer_slug,pk):
account.delete() account.delete()
messages.success(request, _("Account deleted successfully")) messages.success(request, _("Account deleted successfully"))
return redirect("account_list",dealer_slug=dealer_slug) return redirect("account_list", dealer_slug=dealer_slug)
# Sales list # Sales list
@login_required @login_required
@permission_required("inventory.view_lead", raise_exception=True) @permission_required("inventory.view_lead", raise_exception=True)
def sales_list_view(request): def sales_list_view(request, dealer_slug):
""" """
Handles the retrieval and presentation of a paginated list of item transactions for Handles the retrieval and presentation of a paginated list of item transactions for
sales, specific to the logged-in user's entity. Requires the user to have appropriate sales, specific to the logged-in user's entity. Requires the user to have appropriate
@ -3735,7 +3787,7 @@ def sales_list_view(request):
item transactions specific to the user's entity. item transactions specific to the user's entity.
:rtype: HttpResponse :rtype: HttpResponse
""" """
dealer = get_user_type(request) dealer = get_object_or_404(models.Dealer, slug=dealer_slug)
entity = dealer.entity entity = dealer.entity
sale_orders = models.SaleOrder.objects.filter( sale_orders = models.SaleOrder.objects.filter(
@ -3812,7 +3864,7 @@ class EstimateListView(LoginRequiredMixin, PermissionRequiredMixin, ListView):
# @csrf_exempt # @csrf_exempt
@login_required @login_required
@permission_required("django_ledger.add_estimatemodel", raise_exception=True) @permission_required("django_ledger.add_estimatemodel", raise_exception=True)
def create_estimate(request, dealer_slug,slug=None): def create_estimate(request, dealer_slug, slug=None):
""" """
Creates a new estimate based on the provided data and saves it. This function processes Creates a new estimate based on the provided data and saves it. This function processes
a POST request and expects a JSON payload containing details of the estimate such as a POST request and expects a JSON payload containing details of the estimate such as
@ -4005,7 +4057,9 @@ def create_estimate(request, dealer_slug,slug=None):
opportunity.estimate = estimate opportunity.estimate = estimate
opportunity.save() opportunity.save()
url = reverse("estimate_detail", kwargs={"dealer_slug": dealer.slug,"pk": estimate.pk}) url = reverse(
"estimate_detail", kwargs={"dealer_slug": dealer.slug, "pk": estimate.pk}
)
return JsonResponse( return JsonResponse(
{ {
"status": "success", "status": "success",
@ -4111,7 +4165,7 @@ class EstimateDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailView
@login_required @login_required
@permission_required("inventory.add_saleorder", raise_exception=True) @permission_required("inventory.add_saleorder", raise_exception=True)
def create_sale_order(request,dealer_slug, pk): def create_sale_order(request, dealer_slug, pk):
""" """
Creates a sale order for a given estimate and updates associated item and car data. Creates a sale order for a given estimate and updates associated item and car data.
@ -4204,7 +4258,7 @@ class SaleOrderDetail(DetailView):
@login_required @login_required
def preview_sale_order(request, dealer_slug,pk): def preview_sale_order(request, dealer_slug, pk):
""" """
Handles rendering of the sale order preview page for a specific estimate. Handles rendering of the sale order preview page for a specific estimate.
@ -4304,7 +4358,7 @@ class EstimatePreviewView(LoginRequiredMixin, PermissionRequiredMixin, DetailVie
@login_required @login_required
@permission_required("django_ledger.change_estimatemodel", raise_exception=True) @permission_required("django_ledger.change_estimatemodel", raise_exception=True)
def estimate_mark_as(request,dealer_slug, pk): def estimate_mark_as(request, dealer_slug, pk):
""" """
Marks an estimate with a specified status based on the requested action and Marks an estimate with a specified status based on the requested action and
permissions. The marking possibilities include review, approval, rejection, permissions. The marking possibilities include review, approval, rejection,
@ -4327,29 +4381,39 @@ def estimate_mark_as(request,dealer_slug, pk):
if mark == "review": if mark == "review":
if not estimate.can_review(): if not estimate.can_review():
messages.error(request, _("Quotation is not ready for review")) messages.error(request, _("Quotation is not ready for review"))
return redirect("estimate_detail",dealer_slug=dealer.slug, pk=estimate.pk) return redirect(
"estimate_detail", dealer_slug=dealer.slug, pk=estimate.pk
)
estimate.mark_as_review() estimate.mark_as_review()
elif mark == "approved": elif mark == "approved":
if not estimate.can_approve(): if not estimate.can_approve():
messages.error(request, _("Quotation is not ready for approval")) messages.error(request, _("Quotation is not ready for approval"))
return redirect("estimate_detail",dealer_slug=dealer.slug, pk=estimate.pk) return redirect(
"estimate_detail", dealer_slug=dealer.slug, pk=estimate.pk
)
estimate.mark_as_approved() estimate.mark_as_approved()
messages.success(request, _("Quotation approved successfully")) messages.success(request, _("Quotation approved successfully"))
elif mark == "rejected": elif mark == "rejected":
if not estimate.can_cancel(): if not estimate.can_cancel():
messages.error(request, _("Quotation is not ready for rejection")) messages.error(request, _("Quotation is not ready for rejection"))
return redirect("estimate_detail",dealer_slug=dealer.slug, pk=estimate.pk) return redirect(
"estimate_detail", dealer_slug=dealer.slug, pk=estimate.pk
)
estimate.mark_as_canceled() estimate.mark_as_canceled()
messages.success(request, _("Quotation canceled successfully")) messages.success(request, _("Quotation canceled successfully"))
elif mark == "completed": elif mark == "completed":
if not estimate.can_complete(): if not estimate.can_complete():
messages.error(request, _("Quotation is not ready for completion")) messages.error(request, _("Quotation is not ready for completion"))
return redirect("estimate_detail",dealer_slug=dealer.slug, pk=estimate.pk) return redirect(
"estimate_detail", dealer_slug=dealer.slug, pk=estimate.pk
)
elif mark == "canceled": elif mark == "canceled":
if not estimate.can_cancel(): if not estimate.can_cancel():
messages.error(request, _("Quotation is not ready for cancellation")) messages.error(request, _("Quotation is not ready for cancellation"))
return redirect("estimate_detail", dealer_slug=dealer.slug, pk=estimate.pk) return redirect(
"estimate_detail", dealer_slug=dealer.slug, pk=estimate.pk
)
estimate.mark_as_canceled() estimate.mark_as_canceled()
try: try:
car = models.Car.objects.get( car = models.Car.objects.get(
@ -4526,7 +4590,10 @@ class ApprovedInvoiceModelUpdateFormView(
return kwargs return kwargs
def get_success_url(self): def get_success_url(self):
return reverse_lazy("invoice_detail", kwargs={"dealer_slug":self.kwargs["dealer_slug"],"pk": self.object.pk}) return reverse_lazy(
"invoice_detail",
kwargs={"dealer_slug": self.kwargs["dealer_slug"], "pk": self.object.pk},
)
class PaidInvoiceModelUpdateFormView( class PaidInvoiceModelUpdateFormView(
@ -4571,7 +4638,10 @@ class PaidInvoiceModelUpdateFormView(
return kwargs return kwargs
def get_success_url(self): def get_success_url(self):
return reverse_lazy("invoice_detail", kwargs={"dealer_slug":self.kwargs["dealer_slug"],"pk": self.object.pk}) return reverse_lazy(
"invoice_detail",
kwargs={"dealer_slug": self.kwargs["dealer_slug"], "pk": self.object.pk},
)
def form_valid(self, form): def form_valid(self, form):
invoice = form.save() invoice = form.save()
@ -4587,7 +4657,7 @@ class PaidInvoiceModelUpdateFormView(
@login_required @login_required
@permission_required("django_ledger.change_invoicemodel", raise_exception=True) @permission_required("django_ledger.change_invoicemodel", raise_exception=True)
def invoice_mark_as(request,dealer_slug, pk): def invoice_mark_as(request, dealer_slug, pk):
""" """
Marks an invoice as approved if it meets the required conditions. Marks an invoice as approved if it meets the required conditions.
@ -4610,17 +4680,17 @@ def invoice_mark_as(request,dealer_slug, pk):
if mark and mark == "accept": if mark and mark == "accept":
if not invoice.can_approve(): if not invoice.can_approve():
messages.error(request, "invoice is not ready for approval") messages.error(request, "invoice is not ready for approval")
return redirect("invoice_detail",dealer_slug=dealer_slug, pk=invoice.pk) return redirect("invoice_detail", dealer_slug=dealer_slug, pk=invoice.pk)
invoice.mark_as_approved( invoice.mark_as_approved(
entity_slug=dealer.entity.slug, user_model=dealer.entity.admin entity_slug=dealer.entity.slug, user_model=dealer.entity.admin
) )
invoice.save() invoice.save()
return redirect("invoice_detail",dealer_slug=dealer_slug, pk=invoice.pk) return redirect("invoice_detail", dealer_slug=dealer_slug, pk=invoice.pk)
@login_required @login_required
@permission_required("django_ledger.add_invoicemodel", raise_exception=True) @permission_required("django_ledger.add_invoicemodel", raise_exception=True)
def invoice_create(request,dealer_slug, pk): def invoice_create(request, dealer_slug, pk):
""" """
Handles the creation of a new invoice associated with a given estimate. It validates Handles the creation of a new invoice associated with a given estimate. It validates
the submitted data through a form, processes the invoice, updates related models, and the submitted data through a form, processes the invoice, updates related models, and
@ -4636,7 +4706,7 @@ def invoice_create(request,dealer_slug, pk):
creation or renders the invoice creation form template otherwise. creation or renders the invoice creation form template otherwise.
:rtype: HttpResponse :rtype: HttpResponse
""" """
dealer = get_object_or_404(models.Dealer,slug=dealer_slug) dealer = get_object_or_404(models.Dealer, slug=dealer_slug)
estimate = get_object_or_404(EstimateModel, pk=pk) estimate = get_object_or_404(EstimateModel, pk=pk)
entity = dealer.entity entity = dealer.entity
@ -4678,7 +4748,7 @@ def invoice_create(request,dealer_slug, pk):
estimate.save() estimate.save()
invoice.save() invoice.save()
messages.success(request, "Invoice created successfully") messages.success(request, "Invoice created successfully")
return redirect("invoice_detail", dealer_slug=dealer.slug,pk=invoice.pk) return redirect("invoice_detail", dealer_slug=dealer.slug, pk=invoice.pk)
else: else:
print(form.errors) print(form.errors)
form = forms.InvoiceModelCreateForm( form = forms.InvoiceModelCreateForm(
@ -4726,7 +4796,7 @@ class InvoicePreviewView(LoginRequiredMixin, PermissionRequiredMixin, DetailView
permission_required = ["django_ledger.view_invoicemodel"] permission_required = ["django_ledger.view_invoicemodel"]
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
dealer = get_object_or_404(models.Dealer,slug=self.kwargs["dealer_slug"]) dealer = get_object_or_404(models.Dealer, slug=self.kwargs["dealer_slug"])
invoice = kwargs.get("object") invoice = kwargs.get("object")
if invoice.get_itemtxs_data(): if invoice.get_itemtxs_data():
calculator = CarFinanceCalculator(invoice) calculator = CarFinanceCalculator(invoice)
@ -4741,7 +4811,7 @@ class InvoicePreviewView(LoginRequiredMixin, PermissionRequiredMixin, DetailView
@login_required @login_required
@permission_required("django_ledger.add_journalentrymodel", raise_exception=True) @permission_required("django_ledger.add_journalentrymodel", raise_exception=True)
def PaymentCreateView(request,dealer_slug, pk): def PaymentCreateView(request, dealer_slug, pk):
""" """
Handles the creation of a payment entry associated with an invoice or bill. Validates Handles the creation of a payment entry associated with an invoice or bill. Validates
the payment data against the model's current state and reflects the changes in the payment data against the model's current state and reflects the changes in
@ -4764,7 +4834,7 @@ def PaymentCreateView(request,dealer_slug, pk):
redirect to the detail view of the processed invoice or bill, re-render the redirect to the detail view of the processed invoice or bill, re-render the
payment form with error messages or indicate success in payment creation. payment form with error messages or indicate success in payment creation.
""" """
dealer = get_object_or_404(models.Dealer,slug=dealer_slug) dealer = get_object_or_404(models.Dealer, slug=dealer_slug)
invoice = InvoiceModel.objects.filter(pk=pk).first() invoice = InvoiceModel.objects.filter(pk=pk).first()
bill = BillModel.objects.filter(pk=pk).first() bill = BillModel.objects.filter(pk=pk).first()
model = invoice if invoice else bill model = invoice if invoice else bill
@ -4784,7 +4854,7 @@ def PaymentCreateView(request,dealer_slug, pk):
model.mark_as_approved(user_model=entity.admin) model.mark_as_approved(user_model=entity.admin)
if model.amount_paid == model.amount_due: if model.amount_paid == model.amount_due:
messages.error(request, _("fully paid")) messages.error(request, _("fully paid"))
return redirect(redirect_url, dealer_slug=dealer.slug,pk=model.pk) return redirect(redirect_url, dealer_slug=dealer.slug, pk=model.pk)
if model.amount_paid + amount > model.amount_due: if model.amount_paid + amount > model.amount_due:
messages.error(request, _("Amount exceeds due amount")) messages.error(request, _("Amount exceeds due amount"))
return redirect(redirect_url, dealer_slug=dealer.slug, pk=model.pk) return redirect(redirect_url, dealer_slug=dealer.slug, pk=model.pk)
@ -4816,7 +4886,7 @@ def PaymentCreateView(request,dealer_slug, pk):
@login_required @login_required
@permission_required("django_ledger.view_journalentrymodel", raise_exception=True) @permission_required("django_ledger.view_journalentrymodel", raise_exception=True)
def PaymentListView(request,dealer_slug): def PaymentListView(request, dealer_slug):
""" """
Handles the view for listing payment information associated with the journals of a specific Handles the view for listing payment information associated with the journals of a specific
entity. This view is protected to ensure only authenticated and authorized users can entity. This view is protected to ensure only authenticated and authorized users can
@ -4832,7 +4902,7 @@ def PaymentListView(request,dealer_slug):
:return: The rendered HTML response displaying the list of payments. :return: The rendered HTML response displaying the list of payments.
:rtype: HttpResponse :rtype: HttpResponse
""" """
dealer = get_object_or_404(models.Dealer,slug=dealer_slug) dealer = get_object_or_404(models.Dealer, slug=dealer_slug)
entity = dealer.entity entity = dealer.entity
journals = JournalEntryModel.objects.filter(ledger__entity=entity).all() journals = JournalEntryModel.objects.filter(ledger__entity=entity).all()
@ -4846,7 +4916,7 @@ def PaymentListView(request,dealer_slug):
@login_required @login_required
@permission_required("django_ledger.view_journalentrymodel", raise_exception=True) @permission_required("django_ledger.view_journalentrymodel", raise_exception=True)
def PaymentDetailView(request,dealer_slug, pk): def PaymentDetailView(request, dealer_slug, pk):
""" """
This function handles the detail view for a payment by fetching a journal entry This function handles the detail view for a payment by fetching a journal entry
and its associated transactions. It ensures that the request is authenticated and its associated transactions. It ensures that the request is authenticated
@ -4860,7 +4930,7 @@ def PaymentDetailView(request,dealer_slug, pk):
entry and its associated transactions. entry and its associated transactions.
:rtype: HttpResponse :rtype: HttpResponse
""" """
dealer = get_object_or_404(models.Dealer,slug=dealer_slug) dealer = get_object_or_404(models.Dealer, slug=dealer_slug)
journal = JournalEntryModel.objects.filter(pk=pk).first() journal = JournalEntryModel.objects.filter(pk=pk).first()
transactions = ( transactions = (
TransactionModel.objects.filter(journal_entry=journal) TransactionModel.objects.filter(journal_entry=journal)
@ -4876,7 +4946,7 @@ def PaymentDetailView(request,dealer_slug, pk):
@login_required @login_required
@permission_required("django_ledger.change_journalentrymodel", raise_exception=True) @permission_required("django_ledger.change_journalentrymodel", raise_exception=True)
def payment_mark_as_paid(request, dealer_slug,pk): def payment_mark_as_paid(request, dealer_slug, pk):
""" """
Marks an invoice as paid if it meets the conditions of being fully paid and eligible Marks an invoice as paid if it meets the conditions of being fully paid and eligible
for payment. Also ensures that related ledger journal entries are locked and posted for payment. Also ensures that related ledger journal entries are locked and posted
@ -4895,7 +4965,7 @@ def payment_mark_as_paid(request, dealer_slug,pk):
:raises: In case of an exception during the process, an error message is :raises: In case of an exception during the process, an error message is
displayed to the user through Django's messaging framework. displayed to the user through Django's messaging framework.
""" """
dealer = get_object_or_404(models.Dealer,slug=dealer_slug) dealer = get_object_or_404(models.Dealer, slug=dealer_slug)
invoice = get_object_or_404(InvoiceModel, pk=pk) invoice = get_object_or_404(InvoiceModel, pk=pk)
if request.method == "POST": if request.method == "POST":
try: try:
@ -4920,7 +4990,7 @@ def payment_mark_as_paid(request, dealer_slug,pk):
) )
except Exception as e: except Exception as e:
messages.error(request, f"Error: {str(e)}") messages.error(request, f"Error: {str(e)}")
return redirect("invoice_detail",dealer_slug=dealer_slug, pk=invoice.pk) return redirect("invoice_detail", dealer_slug=dealer_slug, pk=invoice.pk)
# activity log # activity log
@ -6651,8 +6721,8 @@ class BillModelCreateView(CreateView):
for_purchase_order = False for_purchase_order = False
for_estimate = False for_estimate = False
def get(self, request, dealer_slug,**kwargs): def get(self, request, dealer_slug, **kwargs):
dealer = get_object_or_404(models.Dealer,slug=dealer_slug) dealer = get_object_or_404(models.Dealer, slug=dealer_slug)
if not request.user.is_authenticated: if not request.user.is_authenticated:
return HttpResponseForbidden() return HttpResponseForbidden()
@ -6665,7 +6735,7 @@ class BillModelCreateView(CreateView):
) )
if not estimate_model.can_bind(): if not estimate_model.can_bind():
return HttpResponseNotFound("404 Not Found") return HttpResponseNotFound("404 Not Found")
return super(BillModelCreateView, self).get(request,dealer_slug, **kwargs) return super(BillModelCreateView, self).get(request, dealer_slug, **kwargs)
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super(BillModelCreateView, self).get_context_data(**kwargs) context = super(BillModelCreateView, self).get_context_data(**kwargs)
@ -6797,17 +6867,29 @@ class BillModelCreateView(CreateView):
po_pk = self.kwargs["po_pk"] po_pk = self.kwargs["po_pk"]
return reverse( return reverse(
"purchase_order_update", "purchase_order_update",
kwargs={"dealer_slug": self.kwargs["dealer_slug"],"entity_slug": entity_slug, "po_pk": po_pk}, kwargs={
"dealer_slug": self.kwargs["dealer_slug"],
"entity_slug": entity_slug,
"po_pk": po_pk,
},
) )
elif self.for_estimate: elif self.for_estimate:
return reverse( return reverse(
"customer-estimate-detail", "customer-estimate-detail",
kwargs={"dealer_slug": self.kwargs["dealer_slug"],"entity_slug": entity_slug, "ce_pk": self.kwargs["ce_pk"]}, kwargs={
"dealer_slug": self.kwargs["dealer_slug"],
"entity_slug": entity_slug,
"ce_pk": self.kwargs["ce_pk"],
},
) )
bill_model: BillModel = self.object bill_model: BillModel = self.object
return reverse( return reverse(
"bill-detail", "bill-detail",
kwargs={"dealer_slug": self.kwargs["dealer_slug"],"entity_slug": entity_slug, "bill_pk": bill_model.uuid}, kwargs={
"dealer_slug": self.kwargs["dealer_slug"],
"entity_slug": entity_slug,
"bill_pk": bill_model.uuid,
},
) )
@ -6823,7 +6905,7 @@ class BillModelDetailViewView(BillModelDetailView):
class BillModelUpdateViewView(BillModelUpdateView): class BillModelUpdateViewView(BillModelUpdateView):
template_name = "bill/bill_update.html" template_name = "bill/bill_update.html"
def post(self, request, dealer_slug,entity_slug,bill_pk, *args, **kwargs): def post(self, request, dealer_slug, entity_slug, bill_pk, *args, **kwargs):
if self.action_update_items: if self.action_update_items:
if not request.user.is_authenticated: if not request.user.is_authenticated:
return HttpResponseForbidden() return HttpResponseForbidden()
@ -6888,7 +6970,9 @@ class BillModelUpdateViewView(BillModelUpdateView):
) )
context = self.get_context_data(itemtxs_formset=itemtxs_formset) context = self.get_context_data(itemtxs_formset=itemtxs_formset)
return self.render_to_response(context=context) return self.render_to_response(context=context)
return super(BillModelUpdateViewView, self).post(request,dealer_slug,entity_slug,bill_pk, **kwargs) return super(BillModelUpdateViewView, self).post(
request, dealer_slug, entity_slug, bill_pk, **kwargs
)
def get_success_url(self): def get_success_url(self):
return reverse( return reverse(
@ -7091,7 +7175,7 @@ class OrderListView(LoginRequiredMixin, PermissionRequiredMixin, ListView):
# email # email
@login_required @login_required
@permission_required("django_ledger.view_estimatemodel", raise_exception=True) @permission_required("django_ledger.view_estimatemodel", raise_exception=True)
def send_email_view(request,dealer_slug, pk): def send_email_view(request, dealer_slug, pk):
""" """
View function to send an email for an estimate. This function allows authenticated and View function to send an email for an estimate. This function allows authenticated and
authorized users to send an email containing the estimate details to the customer. authorized users to send an email containing the estimate details to the customer.
@ -7112,10 +7196,12 @@ def send_email_view(request,dealer_slug, pk):
if not estimate.get_itemtxs_data()[0]: if not estimate.get_itemtxs_data()[0]:
messages.error(request, _("Quotation has no items")) messages.error(request, _("Quotation has no items"))
return redirect("estimate_detail", dealer_slug=dealer_slug,pk=estimate.pk) return redirect("estimate_detail", dealer_slug=dealer_slug, pk=estimate.pk)
link = request.build_absolute_uri( link = request.build_absolute_uri(
reverse_lazy("estimate_preview", kwargs={"dealer_slug":dealer_slug,"pk": estimate.pk}) reverse_lazy(
"estimate_preview", kwargs={"dealer_slug": dealer_slug, "pk": estimate.pk}
)
) )
msg = f""" msg = f"""
@ -7152,7 +7238,7 @@ def send_email_view(request,dealer_slug, pk):
estimate.mark_as_review() estimate.mark_as_review()
messages.success(request, _("Email sent successfully")) messages.success(request, _("Email sent successfully"))
return redirect("estimate_detail", dealer_slug=dealer.slug,pk=estimate.pk) return redirect("estimate_detail", dealer_slug=dealer.slug, pk=estimate.pk)
# errors # errors
@ -8342,7 +8428,7 @@ class JournalEntryListView(LoginRequiredMixin, ListView):
model = JournalEntryModel model = JournalEntryModel
context_object_name = "journal_entries" context_object_name = "journal_entries"
template_name = "ledger/journal_entry/journal_entry_list.html" template_name = "ledger/journal_entry/journal_entry_list.html"
ordering=['-timestamp'] ordering = ["-timestamp"]
def get_queryset(self): def get_queryset(self):
qs = super().get_queryset() qs = super().get_queryset()
@ -8454,12 +8540,14 @@ def JournalEntryTransactionsView(request, pk):
# .order_by("account__code") # .order_by("account__code")
# .all() # .all()
# ) # )
qs=TransactionModel.objects.filter(journal_entry=journal).all() qs = TransactionModel.objects.filter(journal_entry=journal).all()
transactions=qs.annotate( transactions = qs.annotate(
debit_credit_sort_order=Case(When(tx_type='debit',then=Value(0)), debit_credit_sort_order=Case(
When(tx_type='credit',then=Value(1)), When(tx_type="debit", then=Value(0)),
output_field=IntegerField()) When(tx_type="credit", then=Value(1)),
).order_by('debit_credit_sort_order') output_field=IntegerField(),
)
).order_by("debit_credit_sort_order")
return render( return render(
request, request,
"ledger/journal_entry/journal_entry_transactions.html", "ledger/journal_entry/journal_entry_transactions.html",
@ -9074,7 +9162,7 @@ def PurchaseOrderCreateView(request, dealer_slug):
return render(request, "purchase_orders/po_form.html", {"form": form}) return render(request, "purchase_orders/po_form.html", {"form": form})
def InventoryItemCreateView(request,dealer_slug): def InventoryItemCreateView(request, dealer_slug):
dealer = get_object_or_404(models.Dealer, slug=dealer_slug) dealer = get_object_or_404(models.Dealer, slug=dealer_slug)
for_po = request.GET.get("for_po") for_po = request.GET.get("for_po")
entity = dealer.entity entity = dealer.entity
@ -9115,7 +9203,10 @@ def InventoryItemCreateView(request,dealer_slug):
.first() .first()
): ):
messages.error(request, _("Inventory item already exists")) messages.error(request, _("Inventory item already exists"))
return redirect(f"{reverse('inventory_item_create')}?for_po={for_po}", dealer_slug=dealer.slug) return redirect(
f"{reverse('inventory_item_create')}?for_po={for_po}",
dealer_slug=dealer.slug,
)
uom = entity.get_uom_all().get(name="Unit") uom = entity.get_uom_all().get(name="Unit")
entity.create_item_inventory( entity.create_item_inventory(
name=inventory_name, name=inventory_name,
@ -9147,7 +9238,7 @@ def InventoryItemCreateView(request,dealer_slug):
) )
def inventory_items_filter(request,dealer_slug): def inventory_items_filter(request, dealer_slug):
dealer = get_object_or_404(models.Dealer, slug=dealer_slug) dealer = get_object_or_404(models.Dealer, slug=dealer_slug)
make = request.GET.get("make") make = request.GET.get("make")
model = request.GET.get("model") model = request.GET.get("model")
@ -9253,7 +9344,11 @@ class PurchaseOrderUpdateView(PurchaseOrderModelUpdateViewBase):
return HttpResponseRedirect( return HttpResponseRedirect(
redirect_to=reverse( redirect_to=reverse(
"purchase_order_update", "purchase_order_update",
kwargs={"dealer_slug": dealer_slug,"entity_slug": entity_slug, "po_pk": po_pk}, kwargs={
"dealer_slug": dealer_slug,
"entity_slug": entity_slug,
"po_pk": po_pk,
},
) )
) )
return super(PurchaseOrderUpdateView, self).get( return super(PurchaseOrderUpdateView, self).get(
@ -9318,7 +9413,7 @@ class PurchaseOrderUpdateView(PurchaseOrderModelUpdateViewBase):
context=self.get_context_data(itemtxs_formset=itemtxs_formset) context=self.get_context_data(itemtxs_formset=itemtxs_formset)
) )
return super(PurchaseOrderUpdateView, self).post( return super(PurchaseOrderUpdateView, self).post(
request,dealer_slug, entity_slug, *args, **kwargs request, dealer_slug, entity_slug, *args, **kwargs
) )
def get_form(self, form_class=None): def get_form(self, form_class=None):
@ -9349,19 +9444,24 @@ class PurchaseOrderUpdateView(PurchaseOrderModelUpdateViewBase):
class BasePurchaseOrderActionActionView(BasePurchaseOrderActionActionViewBase): class BasePurchaseOrderActionActionView(BasePurchaseOrderActionActionViewBase):
def get_redirect_url(self,dealer_slug, entity_slug, po_pk, *args, **kwargs): def get_redirect_url(self, dealer_slug, entity_slug, po_pk, *args, **kwargs):
return reverse( return reverse(
"purchase_order_update", kwargs={"dealer_slug":dealer_slug,"entity_slug": entity_slug, "po_pk": po_pk} "purchase_order_update",
kwargs={
"dealer_slug": dealer_slug,
"entity_slug": entity_slug,
"po_pk": po_pk,
},
) )
def get(self, request,dealer_slug, entity_slug, po_pk, *args, **kwargs): def get(self, request, dealer_slug, entity_slug, po_pk, *args, **kwargs):
# kwargs["user_model"] = self.request.user # kwargs["user_model"] = self.request.user
dealer = get_object_or_404(models.Dealer, slug=dealer_slug) dealer = get_object_or_404(models.Dealer, slug=dealer_slug)
kwargs["user_model"] = dealer.entity.admin kwargs["user_model"] = dealer.entity.admin
if not self.action_name: if not self.action_name:
raise ImproperlyConfigured("View attribute action_name is required.") raise ImproperlyConfigured("View attribute action_name is required.")
response = super(BasePurchaseOrderActionActionView, self).get( response = super(BasePurchaseOrderActionActionView, self).get(
request,dealer_slug,entity_slug,po_pk, *args, **kwargs request, dealer_slug, entity_slug, po_pk, *args, **kwargs
) )
po_model: PurchaseOrderModel = self.get_object() po_model: PurchaseOrderModel = self.get_object()
@ -9386,7 +9486,9 @@ class PurchaseOrderModelDeleteView(PurchaseOrderModelDeleteViewBase):
message="PO deleted successfully.", message="PO deleted successfully.",
level=messages.SUCCESS, level=messages.SUCCESS,
) )
return reverse("purchase_order_list", kwargs={"dealer_slug": self.kwargs["dealer_slug"]}) return reverse(
"purchase_order_list", kwargs={"dealer_slug": self.kwargs["dealer_slug"]}
)
class PurchaseOrderMarkAsDraftView(BasePurchaseOrderActionActionView): class PurchaseOrderMarkAsDraftView(BasePurchaseOrderActionActionView):
@ -9415,9 +9517,14 @@ class PurchaseOrderMarkAsVoidView(BasePurchaseOrderActionActionView):
##############################bil ##############################bil
class BaseBillActionView(BaseBillActionViewBase): class BaseBillActionView(BaseBillActionViewBase):
def get_redirect_url(self,dealer_slug, entity_slug, bill_pk, *args, **kwargs): def get_redirect_url(self, dealer_slug, entity_slug, bill_pk, *args, **kwargs):
return reverse( return reverse(
"bill-update", kwargs={"dealer_slug": self.kwargs["dealer_slug"],"entity_slug": entity_slug, "bill_pk": bill_pk} "bill-update",
kwargs={
"dealer_slug": self.kwargs["dealer_slug"],
"entity_slug": entity_slug,
"bill_pk": bill_pk,
},
) )
@ -9465,14 +9572,14 @@ class BillModelActionForceMigrateView(BaseBillActionView):
############################################################### ###############################################################
def view_items_inventory(request,dealer_slug, entity_slug, po_pk): def view_items_inventory(request, dealer_slug, entity_slug, po_pk):
dealer = get_object_or_404(models.Dealer, slug=dealer_slug) dealer = get_object_or_404(models.Dealer, slug=dealer_slug)
po = PurchaseOrderModel.objects.get(pk=po_pk) po = PurchaseOrderModel.objects.get(pk=po_pk)
items = po.get_itemtxs_data()[0] items = po.get_itemtxs_data()[0]
items_per_page = 30 items_per_page = 30
paginator = Paginator(items, items_per_page) paginator = Paginator(items, items_per_page)
page_number = request.GET.get('page') page_number = request.GET.get("page")
try: try:
page_obj = paginator.get_page(page_number) page_obj = paginator.get_page(page_number)
except PageNotAnInteger: except PageNotAnInteger:
@ -9482,21 +9589,26 @@ def view_items_inventory(request,dealer_slug, entity_slug, po_pk):
# If page is out of range (e.g. 9999), deliver last page of results. # If page is out of range (e.g. 9999), deliver last page of results.
page_obj = paginator.get_page(paginator.num_pages) page_obj = paginator.get_page(paginator.num_pages)
return render( return render(
request, "purchase_orders/po_upload_cars.html", {"po": po, "items": items,"page_obj":page_obj} request,
"purchase_orders/po_upload_cars.html",
{"po": po, "items": items, "page_obj": page_obj},
) )
def upload_cars(request,dealer_slug, pk=None): def upload_cars(request, dealer_slug, pk=None):
item = None item = None
dealer = get_object_or_404(models.Dealer, slug=dealer_slug) dealer = get_object_or_404(models.Dealer, slug=dealer_slug)
response = redirect("upload_cars",dealer_slug=dealer_slug) response = redirect("upload_cars", dealer_slug=dealer_slug)
if pk: if pk:
item = get_object_or_404(ItemTransactionModel, pk=pk) item = get_object_or_404(ItemTransactionModel, pk=pk)
response = redirect("upload_cars", dealer_slug=dealer_slug,pk=pk) response = redirect("upload_cars", dealer_slug=dealer_slug, pk=pk)
if item.item_model.additional_info.get("uploaded"): if item.item_model.additional_info.get("uploaded"):
messages.add_message(request, messages.ERROR, "Item already uploaded.") messages.add_message(request, messages.ERROR, "Item already uploaded.")
return redirect( return redirect(
"view_items_inventory",dealer_slug=dealer_slug, entity_slug=dealer.entity.slug, po_pk=item.po_model.pk "view_items_inventory",
dealer_slug=dealer_slug,
entity_slug=dealer.entity.slug,
po_pk=item.po_model.pk,
) )
if request.method == "POST": if request.method == "POST":
@ -9506,7 +9618,9 @@ def upload_cars(request,dealer_slug, pk=None):
if item: if item:
item = ItemTransactionModel.objects.get(pk=pk) item = ItemTransactionModel.objects.get(pk=pk)
data = [x.strip() for x in item.item_model.name.split("||")] data = [x.strip() for x in item.item_model.name.split("||")]
make = models.CarMake.objects.filter(is_sa_import=True).get(name=data[0]) make = models.CarMake.objects.filter(is_sa_import=True).get(
name=data[0]
)
model = make.carmodel_set.get(name=data[1]) model = make.carmodel_set.get(name=data[1])
trim = models.CarTrim.objects.filter( trim = models.CarTrim.objects.filter(
name=data[3], id_car_serie__id_car_model=model.id_car_model name=data[3], id_car_serie__id_car_model=model.id_car_model
@ -9541,7 +9655,7 @@ def upload_cars(request,dealer_slug, pk=None):
if not csv_file.name.endswith(".csv"): if not csv_file.name.endswith(".csv"):
messages.error(request, "Please upload a CSV file") messages.error(request, "Please upload a CSV file")
return redirect("upload_cars",dealer_slug=dealer_slug) return redirect("upload_cars", dealer_slug=dealer_slug)
try: try:
# Read the file content # Read the file content
file_content = csv_file.read().decode("utf-8") file_content = csv_file.read().decode("utf-8")
@ -9644,5 +9758,3 @@ class InventoryListView(InventoryListViewBase):
entity_slug=dealer.entity.slug, entity_slug=dealer.entity.slug,
) )
return super().get_queryset() return super().get_queryset()

View File

@ -232,26 +232,26 @@
{% endfor %} {% endfor %}
</tbody> </tbody>
</table> </table>
</div>
{% if page_obj.paginator.num_pages > 1 %}
<div class="d-flex justify-content-end mt-3">
<div class="d-flex">
{% include 'partials/pagination.html'%}
</div>
</div>
{% endif %}
{% else %}
<tr>
<td colspan="6" class="text-center">{% trans "No Lead Yet" %}</td>
</tr>
{% endif %}
</div>
</div> </div>
{% if page_obj.paginator.num_pages > 1 %}
<div class="d-flex justify-content-end mt-3">
<div class="d-flex">
{% include 'partials/pagination.html'%}
</div>
</div>
{% endif %}
{% else %}
<tr>
<td colspan="6" class="text-center">{% trans "No Lead Yet" %}</td>
</tr>
{% endif %}
</div> </div>
</div>
</div>
{% endblock %} {% endblock %}

View File

@ -90,14 +90,14 @@
<div id="opportunities-grid" class="row g-4 px-2 px-lg-4 mt-1"> <div id="opportunities-grid" class="row g-4 px-2 px-lg-4 mt-1">
{% include 'crm/opportunities/partials/opportunity_grid.html' %} {% include 'crm/opportunities/partials/opportunity_grid.html' %}
</div> </div>
{% if page_obj.paginator.num_pages > 1 %} {% if page_obj.paginator.num_pages > 1 %}
<div class="d-flex justify-content-end mt-3"> <div class="d-flex justify-content-end mt-3">
<div class="d-flex"> <div class="d-flex">
{% include 'partials/pagination.html'%} {% include 'partials/pagination.html'%}
</div> </div>
</div> </div>
{% endif %} {% endif %}
{% endblock %} {% endblock %}

View File

@ -109,15 +109,15 @@
</tbody> </tbody>
{% endif %} {% endif %}
</table> </table>
{% if page_obj.paginator.num_pages > 1 %} {% if page_obj.paginator.num_pages > 1 %}
<div class="d-flex justify-content-end mt-3"> <div class="d-flex justify-content-end mt-3">
<div class="d-flex"> <div class="d-flex">
{% include 'partials/pagination.html'%} {% include 'partials/pagination.html'%}
</div> </div>
</div> </div>
{% endif %} {% endif %}
{% include 'modal/delete_modal.html' %} {% include 'modal/delete_modal.html' %}
{% endblock %} {% endblock %}

View File

@ -47,11 +47,11 @@
</div> </div>
{% if page_obj.paginator.num_pages > 1 %} {% if page_obj.paginator.num_pages > 1 %}
<div class="d-flex justify-content-end mt-3"> <div class="d-flex justify-content-end mt-3">
<div class="d-flex"> <div class="d-flex">
{% include 'partials/pagination.html'%} {% include 'partials/pagination.html'%}
</div> </div>
</div> </div>
{% endif %} {% endif %}

View File

@ -118,7 +118,7 @@
</li> </li>
{% endif %} {% endif %}
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="{% url 'sales_list' %}"> <a class="nav-link" href="{% url 'sales_list' request.dealer.slug %}">
<div class="d-flex align-items-center"> <div class="d-flex align-items-center">
<span class="nav-link-icon"><span class="fas fa-money-check"></span></span><span class="nav-link-text">{% trans "Sales Orders"|capfirst %}</span> <span class="nav-link-icon"><span class="fas fa-money-check"></span></span><span class="nav-link-text">{% trans "Sales Orders"|capfirst %}</span>
</div> </div>

View File

@ -163,14 +163,14 @@
<tr> <tr>
<th>{% trans "Custom Card" %}</th> <th>{% trans "Custom Card" %}</th>
<td> <td>
<button type="button" <button type="button"
class="btn btn-sm btn-phoenix-success" class="btn btn-sm btn-phoenix-success"
data-bs-toggle="modal" data-bs-toggle="modal"
data-bs-target="#mainModal" data-bs-target="#mainModal"
hx-get="{% url 'add_custom_card' request.dealer.slug car.slug %}" hx-get="{% url 'add_custom_card' request.dealer.slug car.slug %}"
hx-target=".main-modal-body" hx-target=".main-modal-body"
hx-swap="innerHTML" hx-swap="innerHTML"
>{% trans 'Add' %}</button> >{% trans 'Add' %}</button>
</td> </td>
</tr> </tr>
{% endif %} {% endif %}
@ -196,7 +196,7 @@
hx-get="{% url 'add_registration' request.dealer.slug car.slug %}" hx-get="{% url 'add_registration' request.dealer.slug car.slug %}"
hx-target=".main-modal-body" hx-target=".main-modal-body"
hx-swap="innerHTML" hx-swap="innerHTML"
>{% trans 'Add' %}</button> >{% trans 'Add' %}</button>
</td> </td>
</tr> </tr>

View File

@ -25,83 +25,83 @@
{% endblock customCSS %} {% endblock customCSS %}
{% block content %} {% block content %}
<div class="container-fluid" id="projectSummary"> <div class="container-fluid" id="projectSummary">
<div class="row g-3 justify-content-between align-items-end mb-4"> <div class="row g-3 justify-content-between align-items-end mb-4">
<div class="col-12 col-sm-auto"> <div class="col-12 col-sm-auto">
<h2 class="text-body-emphasis fw-bold mb-0">{{ _("Inventory") }}</h2> <h2 class="text-body-emphasis fw-bold mb-0">{{ _("Inventory") }}</h2>
</div> </div>
</div> </div>
<div class="row g-3 justify-content-between align-items-end mb-2"> <div class="row g-3 justify-content-between align-items-end mb-2">
<div class="col-4 col-sm-auto"> <div class="col-4 col-sm-auto">
<ul <ul
class="nav nav-links mx-n2" class="nav nav-links mx-n2"
hx-boost="true" hx-boost="true"
hx-push-url="false" hx-push-url="false"
hx-target=".table-responsive" hx-target=".table-responsive"
hx-select=".table-responsive" hx-select=".table-responsive"
hx-swap="innerHTML show:window:top" hx-swap="innerHTML show:window:top"
hx-indicator=".htmx-indicator" hx-indicator=".htmx-indicator"
hx-on::before-request="on_before_request()" hx-on::before-request="on_before_request()"
hx-on::after-request="on_after_request()"> hx-on::after-request="on_after_request()">
<li class="nav-item"> <li class="nav-item">
<a class="nav-link px-2 py-1 active" aria-current="page" href="{% url 'car_list' request.dealer.slug %}"><span>{{ _("All") }}</span><span class="text-body-tertiary fw-semibold">({{ stats.all }})</span></a> <a class="nav-link px-2 py-1 active" aria-current="page" href="{% url 'car_list' request.dealer.slug %}"><span>{{ _("All") }}</span><span class="text-body-tertiary fw-semibold">({{ stats.all }})</span></a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link px-2 py-1" href="{% url 'car_list' request.dealer.slug %}?status=available"><span>{{ _("Available") }}</span><span class="text-body-tertiary fw-semibold">({{ stats.available }})</span></a> <a class="nav-link px-2 py-1" href="{% url 'car_list' request.dealer.slug %}?status=available"><span>{{ _("Available") }}</span><span class="text-body-tertiary fw-semibold">({{ stats.available }})</span></a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link px-2 py-1" href="{% url 'car_list' request.dealer.slug %}?status=reserved"><span>{{ _("Reserved") }}</span><span class="text-body-tertiary fw-semibold">({{ stats.reserved }})</span></a> <a class="nav-link px-2 py-1" href="{% url 'car_list' request.dealer.slug %}?status=reserved"><span>{{ _("Reserved") }}</span><span class="text-body-tertiary fw-semibold">({{ stats.reserved }})</span></a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link px-2 py-1" href="{% url 'car_list' request.dealer.slug %}?status=transfer"><span>{{ _("Transfer") }}</span><span class="text-body-tertiary fw-semibold">({{ stats.transfer }})</span></a> <a class="nav-link px-2 py-1" href="{% url 'car_list' request.dealer.slug %}?status=transfer"><span>{{ _("Transfer") }}</span><span class="text-body-tertiary fw-semibold">({{ stats.transfer }})</span></a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link px-2 py-1" href="{% url 'car_list' request.dealer.slug %}?status=sold"><span>{{ _("Sold") }}</span><span class="text-body-tertiary fw-semibold">({{ stats.sold }})</span></a> <a class="nav-link px-2 py-1" href="{% url 'car_list' request.dealer.slug %}?status=sold"><span>{{ _("Sold") }}</span><span class="text-body-tertiary fw-semibold">({{ stats.sold }})</span></a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<button hx-on:click="toggle_filter()" class="btn btn-sm btn-phoenix-primary px-2 py-1"> <button hx-on:click="toggle_filter()" class="btn btn-sm btn-phoenix-primary px-2 py-1">
<span><span class="fa fa-filter me-1"></span>{{ _("Filter") }}</span> <span><span class="fa fa-filter me-1"></span>{{ _("Filter") }}</span>
<span class="fas fa-caret-down fs-9 ms-1 filter-icon"></span> <span class="fas fa-caret-down fs-9 ms-1 filter-icon"></span>
</button> </button>
</li> </li>
</ul> </ul>
</div> </div>
<div class="col-4 col-sm-auto"> <div class="col-4 col-sm-auto">
<form hx-boost="true" action="{% url 'bulk_update_car_price' %}" method="post" hx-include=".car-checkbox" class="update-price-form d-flex flex-row align-items-center ms-auto d-none" style="float: right;"> <form hx-boost="true" action="{% url 'bulk_update_car_price' %}" method="post" hx-include=".car-checkbox" class="update-price-form d-flex flex-row align-items-center ms-auto d-none" style="float: right;">
{% csrf_token %} {% csrf_token %}
<div class="input-group"> <div class="input-group">
<input class="form-control" type="number" placeholder='{{ _("Price") }}' name="price" aria-label="Price" id="price" /> <input class="form-control" type="number" placeholder='{{ _("Price") }}' name="price" aria-label="Price" id="price" />
<button class="btn btn-sm btn-phoenix-primary" type="submit">{{ _("Update") }}</button> <button class="btn btn-sm btn-phoenix-primary" type="submit">{{ _("Update") }}</button>
</div> </div>
</form>
</div>
<div class="col-4 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">
<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' request.dealer.slug %}"
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> </form>
</div>
<div class="col-4 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">
<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' request.dealer.slug %}"
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>
</div> </div>
</div> </div>
</div>
</div> </div>
<div class="row"> <div class="row">
<div class="d-flex align-items-center d-none filter"> <div class="d-flex align-items-center d-none filter">
@ -165,147 +165,147 @@
{{ _("Search") }} {{ _("Search") }}
</button> </button>
</div> </div>
<div class="table-responsive scrollbar"> <div class="table-responsive scrollbar">
<div class="d-flex flex-wrap align-items-center justify-content-between py-3 pe-0 fs-9"> <div class="d-flex flex-wrap align-items-center justify-content-between py-3 pe-0 fs-9">
<div <div
class="d-flex" class="d-flex"
hx-boost="true" hx-boost="true"
hx-push-url="false" hx-push-url="false"
hx-include=".make,.model,.year,.car_status" hx-include=".make,.model,.year,.car_status"
hx-target=".table-responsive" hx-target=".table-responsive"
hx-select=".table-responsive" hx-select=".table-responsive"
hx-swap="innerHTML show:window:top" hx-swap="innerHTML show:window:top"
hx-indicator=".htmx-indicator" hx-indicator=".htmx-indicator"
hx-on::before-request="on_before_request()" hx-on::before-request="on_before_request()"
hx-on::after-request="on_after_request()"></div> hx-on::after-request="on_after_request()"></div>
<div class="w-100 list table-responsive" > <div class="w-100 list table-responsive" >
<div class="form-check"> <div class="form-check">
<input class="form-check-input ms-4" type="checkbox" id="select-all" /> <span class="ms-1 text-body-tertiary">{{ _("Select All") }}</span> <input class="form-check-input ms-4" type="checkbox" id="select-all" /> <span class="ms-1 text-body-tertiary">{{ _("Select All") }}</span>
</div> </div>
{% for car in cars %} {% for car in cars %}
<div class="card border mb-3 py-0 px-0" id="project-list-table-body"> <div class="card border mb-3 py-0 px-0" id="project-list-table-body">
<div class="card-body"> <div class="card-body">
<div class="row align-items-center"> <div class="row align-items-center">
<div class="col-auto"> <div class="col-auto">
<div class="form-check"> <div class="form-check">
<input class="form-check-input car-checkbox" type="checkbox" name="car" value="{{ car.pk }}" id="car-{{car.pk}}" /> <input class="form-check-input car-checkbox" type="checkbox" name="car" value="{{ car.pk }}" id="car-{{car.pk}}" />
</div>
</div> </div>
</div>
<!-- Vehicle Image/Icon --> <!-- Vehicle Image/Icon -->
<div class="col-auto"> <div class="col-auto">
<div class="avatar avatar-3xl"> <div class="avatar avatar-3xl">
<img class="rounded-soft shadow shadow-lg" src="{% static 'images/cars/' %}{{ car.vin }}.png" alt="{{ car.vin }}" /> <img class="rounded-soft shadow shadow-lg" src="{% static 'images/cars/' %}{{ car.vin }}.png" alt="{{ car.vin }}" />
</div>
</div> </div>
</div>
<!-- Vehicle Details --> <!-- Vehicle Details -->
<div class="col"> <div class="col">
<div class="row"> <div class="row">
<!-- Make/Model/Specs --> <!-- Make/Model/Specs -->
<div class="col-md-4"> <div class="col-md-4">
<h5 class="text-body-emphasis fw-bold">{{ car.year }}</h5> <h5 class="text-body-emphasis fw-bold">{{ car.year }}</h5>
<p class="text-body-emphasis fw-bold"> <p class="text-body-emphasis fw-bold">
<a href="{% url 'car_detail' request.dealer.slug car.slug %}" class="text-decoration-none text-body-emphasis"> <a href="{% url 'car_detail' request.dealer.slug car.slug %}" class="text-decoration-none text-body-emphasis">
{{ car.id_car_make.get_local_name }} {{ car.id_car_make.get_local_name }}
</a> </a>
<small>{{ car.id_car_model.get_local_name }}</small> <small>{{ car.id_car_model.get_local_name }}</small>
</p> </p>
<small class="text-body-secondary" dir="ltr"> <small class="text-body-secondary" dir="ltr">
{{ car.id_car_trim }} {{ car.id_car_trim }}
</small> </small>
</div> </div>
<!-- Color and Date --> <!-- Color and Date -->
<div class="col-md-3"> <div class="col-md-3">
<p class="text-body mb-1"> <p class="text-body mb-1">
{{ car.colors.exterior.get_local_name }} {{ car.colors.exterior.get_local_name }}
</p> </p>
<small class="text-body-secondary"> <small class="text-body-secondary">
{{ car.receiving_date|naturalday|capfirst }} {{ car.receiving_date|naturalday|capfirst }}
</small> </small>
</div> </div>
<!-- Status Badge --> <!-- Status Badge -->
<div class="col-md-3"> <div class="col-md-3">
{% if car.status == "available" %} {% if car.status == "available" %}
<span class="badge badge-phoenix fs-10 badge-phoenix-success text-uppercase px-3 py-2">{{ _("Available") }}</span> <span class="badge badge-phoenix fs-10 badge-phoenix-success text-uppercase px-3 py-2">{{ _("Available") }}</span>
{% elif car.status == "reserved" %} {% elif car.status == "reserved" %}
<span class="badge badge-phoenix fs-10 badge-phoenix-warning text-uppercase px-3 py-2">{{ _("Reserved") }}</span> <span class="badge badge-phoenix fs-10 badge-phoenix-warning text-uppercase px-3 py-2">{{ _("Reserved") }}</span>
{% elif car.status == "sold" %} {% elif car.status == "sold" %}
<span class="badge badge-phoenix fs-10 badge-phoenix-danger text-uppercase px-3 py-2">{{ _("Sold") }}</span> <span class="badge badge-phoenix fs-10 badge-phoenix-danger text-uppercase px-3 py-2">{{ _("Sold") }}</span>
{% elif car.status == "transfer" %} {% elif car.status == "transfer" %}
<span class="badge badge-phoenix fs-10 badge-phoenix-info text-uppercase px-3 py-2">{{ _("Transfer") }}</span> <span class="badge badge-phoenix fs-10 badge-phoenix-info text-uppercase px-3 py-2">{{ _("Transfer") }}</span>
{% endif %} {% endif %}
</div> </div>
<!-- Ready Status --> <!-- Ready Status -->
<div class="col-md-2"> <div class="col-md-2">
<div class="d-flex align-items-center"> <div class="d-flex align-items-center">
<span class="fs-10 fw-light me-2">{{ _("Inventory Ready") }}</span> <span class="fs-10 fw-light me-2">{{ _("Inventory Ready") }}</span>
{% if car.ready %} {% if car.ready %}
<span class="badge bg-success rounded-circle p-1 me-2"> <span class="badge bg-success rounded-circle p-1 me-2">
<span class="visually-hidden">Ready</span> <span class="visually-hidden">Ready</span>
</span> </span>
<span class="text-success fw-bold">{{ _("Yes") }}</span> <span class="text-success fw-bold">{{ _("Yes") }}</span>
{% else %} {% else %}
<span class="badge bg-danger rounded-circle p-1 me-2"> <span class="badge bg-danger rounded-circle p-1 me-2">
<span class="visually-hidden">Not Ready</span> <span class="visually-hidden">Not Ready</span>
</span> </span>
<span class="text-danger fw-bold">{{ _("No") }}</span> <span class="text-danger fw-bold">{{ _("No") }}</span>
{% endif %} {% endif %}
</div>
</div> </div>
</div> </div>
</div> </div>
</div>
<!-- Action Menu --> <!-- Action Menu -->
<div class="col-auto"> <div class="col-auto">
<div class="btn-reveal-trigger position-static"> <div class="btn-reveal-trigger position-static">
<button <button
class="btn btn-sm dropdown-toggle dropdown-caret-none transition-none btn-reveal fs-10" class="btn btn-sm dropdown-toggle dropdown-caret-none transition-none btn-reveal fs-10"
type="button" type="button"
data-bs-toggle="dropdown" data-bs-toggle="dropdown"
data-boundary="window" data-boundary="window"
aria-haspopup="true" aria-haspopup="true"
aria-expanded="false" aria-expanded="false"
data-bs-reference="parent"> data-bs-reference="parent">
<span class="fas fa-ellipsis-h fs-10"></span> <span class="fas fa-ellipsis-h fs-10"></span>
</button> </button>
<div class="dropdown-menu dropdown-menu-end py-2"> <div class="dropdown-menu dropdown-menu-end py-2">
<a class="dropdown-item" href="{% url 'car_detail' request.dealer.slug car.slug %}"> <span class="fas fa-eye me-2"></span>{{ _("View") }} </a> <a class="dropdown-item" href="{% url 'car_detail' request.dealer.slug car.slug %}"> <span class="fas fa-eye me-2"></span>{{ _("View") }} </a>
<a class="dropdown-item" href="{% url 'car_update' request.dealer.slug car.slug %}"> <span class="fas fa-edit me-2"></span>{{ _("Edit") }} </a> <a class="dropdown-item" href="{% url 'car_update' request.dealer.slug car.slug %}"> <span class="fas fa-edit me-2"></span>{{ _("Edit") }} </a>
<div class="dropdown-divider"></div> <div class="dropdown-divider"></div>
<a class="dropdown-item text-danger" href="{% url 'car_delete' request.dealer.slug car.slug %}"> <span class="fas fa-trash me-2"></span>{{ _("Remove") }} </a> <a class="dropdown-item text-danger" href="{% url 'car_delete' request.dealer.slug car.slug %}"> <span class="fas fa-trash me-2"></span>{{ _("Remove") }} </a>
</div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
{% empty %} </div>
<div class="text-center py-5"> {% empty %}
<div class="text-body-secondary"> <div class="text-center py-5">
<span class="fas fa-car fa-4x mb-3 opacity-50"></span> <div class="text-body-secondary">
<h4 class="text-body-secondary">{{ _("No vehicles found") }}</h4> <span class="fas fa-car fa-4x mb-3 opacity-50"></span>
<p class="text-body-tertiary">{{ _("Try adjusting your search criteria or filters") }}</p> <h4 class="text-body-secondary">{{ _("No vehicles found") }}</h4>
</div> <p class="text-body-tertiary">{{ _("Try adjusting your search criteria or filters") }}</p>
</div> </div>
{% endfor %} </div>
</div> {% endfor %}
</div>
{% if page_obj.paginator.num_pages > 1 %}
<div class="d-flex justify-content-end mt-3">
<div class="d-flex">
{% include 'partials/pagination.html'%}
</div>
</div>
{% endif %}
</div> </div>
</div> </div>
{% if page_obj.paginator.num_pages > 1 %}
<div class="d-flex justify-content-end mt-3">
<div class="d-flex">
{% include 'partials/pagination.html'%}
</div>
</div>
{% endif %}
</div>
</div> </div>
</div> </div>
</div>
{% endblock %} {% endblock %}
{% block customJS %} {% block customJS %}

View File

@ -87,14 +87,14 @@
</div> </div>
{% if is_paginated %} {% if is_paginated %}
<div class="d-flex justify-content-end mt-3"> <div class="d-flex justify-content-end mt-3">
<div class="d-flex"> <div class="d-flex">
{% include 'partials/pagination.html'%} {% include 'partials/pagination.html'%}
</div> </div>
</div> </div>
{% endif %} {% endif %}
</div> </div>
</div> </div>
{% endblock %} {% endblock %}

View File

@ -18,7 +18,7 @@
<tr class="hover-actions-trigger"> <tr class="hover-actions-trigger">
<td class="ps-2 fw-medium">{{ i.item_model__name }}</td> <td class="ps-2 fw-medium">{{ i.item_model__name }}</td>
<td class="ps-2 fw-medium">{{ i.item_model__name }}</td> <td class="ps-2 fw-medium">{{ i.item_model__name }}</td>
<td class="text-center">{{ i.item_model__uom__name }}</td> <td class="text-center">{{ i.item_model__uom__name }}</td>
<td class="text-end pe-3">{{ i.total_quantity | floatformat:3 }}</td> <td class="text-end pe-3">{{ i.total_quantity | floatformat:3 }}</td>

View File

@ -61,16 +61,16 @@
</tbody> </tbody>
</table> </table>
</div> </div>
{% if page_obj.paginator.num_pages > 1 %} {% if page_obj.paginator.num_pages > 1 %}
<div class="d-flex justify-content-end mt-3"> <div class="d-flex justify-content-end mt-3">
<div class="d-flex"> <div class="d-flex">
{% include 'partials/pagination.html'%} {% include 'partials/pagination.html'%}
</div> </div>
</div> </div>
{% endif %} {% endif %}
{% endif %} {% endif %}
</div> </div>
{% endblock %} {% endblock %}

View File

@ -62,16 +62,16 @@
</tbody> </tbody>
</table> </table>
</div> </div>
{% if page_obj.paginator.num_pages > 1 %} {% if page_obj.paginator.num_pages > 1 %}
<div class="d-flex justify-content-end mt-3"> <div class="d-flex justify-content-end mt-3">
<div class="d-flex"> <div class="d-flex">
{% include 'partials/pagination.html'%} {% include 'partials/pagination.html'%}
</div> </div>
</div> </div>
{% endif %} {% endif %}
{% endif %} {% endif %}
</div> </div>
{% endblock %} {% endblock %}

View File

@ -52,14 +52,14 @@
</tbody> </tbody>
</table> </table>
</div> </div>
{% if page_obj.paginator.num_pages > 1 %} {% if page_obj.paginator.num_pages > 1 %}
<div class="d-flex justify-content-end mt-3"> <div class="d-flex justify-content-end mt-3">
<div class="d-flex"> <div class="d-flex">
{% include 'partials/pagination.html'%} {% include 'partials/pagination.html'%}
</div> </div>
</div> </div>
{% endif %} {% endif %}
{% endif %} {% endif %}

View File

@ -96,16 +96,16 @@
</tbody> </tbody>
</table> </table>
</div> </div>
{% if page_obj.paginator.num_pages > 1 %} {% if page_obj.paginator.num_pages > 1 %}
<div class="d-flex justify-content-end mt-3"> <div class="d-flex justify-content-end mt-3">
<div class="d-flex"> <div class="d-flex">
{% include 'partials/pagination.html'%} {% include 'partials/pagination.html'%}
</div> </div>
</div> </div>
{% endif %} {% endif %}
</div> </div>
{% endblock %} {% endblock %}

View File

@ -113,16 +113,16 @@
</tbody> </tbody>
</table> </table>
</div> </div>
{% if page_obj.paginator.num_pages > 1 %} {% if page_obj.paginator.num_pages > 1 %}
<div class="d-flex justify-content-end mt-3"> <div class="d-flex justify-content-end mt-3">
<div class="d-flex"> <div class="d-flex">
{% include 'partials/pagination.html'%} {% include 'partials/pagination.html'%}
</div> </div>
</div> </div>
{% endif %} {% endif %}
</div> </div>
<!--test--> <!--test-->

View File

@ -42,13 +42,13 @@
</tbody> </tbody>
</table> </table>
</div> </div>
{% if page_obj.paginator.num_pages > 1 %} {% if page_obj.paginator.num_pages > 1 %}
<div class="d-flex justify-content-end mt-3"> <div class="d-flex justify-content-end mt-3">
<div class="d-flex"> <div class="d-flex">
{% include 'partials/pagination.html'%} {% include 'partials/pagination.html'%}
</div> </div>
</div> </div>
{% endif %} {% endif %}

View File

@ -121,15 +121,15 @@
</tbody> </tbody>
</table> </table>
</div> </div>
{% if page_obj.paginator.num_pages > 1 %} {% if page_obj.paginator.num_pages > 1 %}
<div class="d-flex justify-content-end mt-3"> <div class="d-flex justify-content-end mt-3">
<div class="d-flex">
{% include 'partials/pagination.html'%}
</div>
<div class="d-flex">
{% include 'partials/pagination.html'%}
</div> </div>
</div>
{% endif %} {% endif %}
</div> </div>
{% endblock %} {% endblock %}

View File

@ -24,9 +24,9 @@
</div> </div>
</div> </div>
<div class="col-auto"> <div class="col-auto">
<div class="d-flex"> <div class="d-flex">
{% include 'partials/search_box.html' %} {% include 'partials/search_box.html' %}
</div> </div>
</div> </div>
</div> </div>
@ -123,16 +123,16 @@
</tbody> </tbody>
</table> </table>
</div> </div>
{% if page_obj.paginator.num_pages > 1 %} {% if page_obj.paginator.num_pages > 1 %}
<div class="d-flex justify-content-end mt-3"> <div class="d-flex justify-content-end mt-3">
<div class="d-flex"> <div class="d-flex">
{% include 'partials/pagination.html'%} {% include 'partials/pagination.html'%}
</div> </div>
</div> </div>
{% endif %} {% endif %}
</div> </div>
</section> </section>
{% endblock %} {% endblock %}

View File

@ -1,90 +1,90 @@
{% load i18n static %} {% load i18n static %}
<div class="d-flex justify-content-between align-items-center mt-4"> <div class="d-flex justify-content-between align-items-center mt-4">
<div class="text-body-secondary"> <div class="text-body-secondary">
{{ _("Showing") }} {{ page_obj.start_index }} {{ _("to") }} {{ page_obj.end_index }} {{ _("Showing") }} {{ page_obj.start_index }} {{ _("to") }} {{ page_obj.end_index }}
{{ _("of") }} {{ page_obj.paginator.count }} {{ _("results") }} {{ _("of") }} {{ page_obj.paginator.count }} {{ _("results") }}
</div> </div>
<nav aria-label="Page navigation"> <nav aria-label="Page navigation">
<ul class="pagination mb-0"> <ul class="pagination mb-0">
{# First Page Link #} {# First Page Link #}
{% if page_obj.has_previous %} {% if page_obj.has_previous %}
<li class="page-item rounded-md overflow-hidden"> <li class="page-item rounded-md overflow-hidden">
<a class="page-link px-3 py-2 border border-gray-300 bg-white text-blue-600 hover:bg-gray-100 transition-colors duration-200" href="?page=1{% if q %}&q={{q}}{% endif %}" aria-label="{% trans 'First' %}"> <a class="page-link px-3 py-2 border border-gray-300 bg-white text-blue-600 hover:bg-gray-100 transition-colors duration-200" href="?page=1{% if q %}&q={{q}}{% endif %}" aria-label="{% trans 'First' %}">
<span class="fas fa-angle-double-left" aria-hidden="true"></span> <span class="fas fa-angle-double-left" aria-hidden="true"></span>
</a>
</li>
{% else %}
<li class="page-item disabled rounded-md overflow-hidden">
<span class="page-link px-3 py-2 border border-gray-200 bg-gray-50 text-gray-400 cursor-not-allowed">
<span class="fas fa-angle-double-left" aria-hidden="true"></span>
</span>
</li>
{% endif %}
{# Previous Page Link #}
{% if page_obj.has_previous %}
<li class="page-item rounded-md overflow-hidden">
<a class="page-link px-3 py-2 border border-gray-300 bg-white text-blue-600 hover:bg-gray-100 transition-colors duration-200" href="?page={{ page_obj.previous_page_number }}{% if q %}&q={{q}}{% endif %}" aria-label="{% trans 'Previous' %}">
<span class="fas fa-chevron-left" aria-hidden="true"></span>
</a>
</li>
{% else %}
<li class="page-item disabled rounded-md overflow-hidden">
<span class="page-link px-3 py-2 border border-gray-200 bg-gray-50 text-gray-400 cursor-not-allowed">
<span class="fas fa-chevron-left" aria-hidden="true"></span>
</span>
</li>
{% endif %}
{# Page Numbers #}
{% for num in page_obj.paginator.page_range %}
{% if num == 1 or num == page_obj.paginator.num_pages or num >= page_obj.number|add:-2 and num <= page_obj.number|add:2 %}
<li class="page-item {% if num == page_obj.number %}active{% endif %}">
<a class="page-link" {% if num == page_obj.number %}aria-current="page"{% endif %}
href="?page={{ num }}{% if q %}&q={{q}}{% endif %}">
{{ num }}
</a> </a>
</li> </li>
{% else %}
<li class="page-item disabled rounded-md overflow-hidden">
<span class="page-link px-3 py-2 border border-gray-200 bg-gray-50 text-gray-400 cursor-not-allowed">
<span class="fas fa-angle-double-left" aria-hidden="true"></span>
</span>
</li>
{% endif %} {% endif %}
{% endfor %}
{# Previous Page Link #}
{% if page_obj.has_previous %}
<li class="page-item rounded-md overflow-hidden">
<a class="page-link px-3 py-2 border border-gray-300 bg-white text-blue-600 hover:bg-gray-100 transition-colors duration-200" href="?page={{ page_obj.previous_page_number }}{% if q %}&q={{q}}{% endif %}" aria-label="{% trans 'Previous' %}">
<span class="fas fa-chevron-left" aria-hidden="true"></span>
</a>
</li>
{% else %}
<li class="page-item disabled rounded-md overflow-hidden">
<span class="page-link px-3 py-2 border border-gray-200 bg-gray-50 text-gray-400 cursor-not-allowed">
<span class="fas fa-chevron-left" aria-hidden="true"></span>
</span>
</li>
{% endif %}
{# Page Numbers #}
{% for num in page_obj.paginator.page_range %}
{% if num == 1 or num == page_obj.paginator.num_pages or num >= page_obj.number|add:-2 and num <= page_obj.number|add:2 %}
<li class="page-item {% if num == page_obj.number %}active{% endif %}">
<a class="page-link" {% if num == page_obj.number %}aria-current="page"{% endif %}
href="?page={{ num }}{% if q %}&q={{q}}{% endif %}">
{{ num }}
</a>
</li>
{% endif %}
{% endfor %}
{# Next Page Link #} {# Next Page Link #}
{% if page_obj.has_next %} {% if page_obj.has_next %}
<li class="page-item rounded-md overflow-hidden"> <li class="page-item rounded-md overflow-hidden">
<a class="page-link px-3 py-2 border border-gray-300 bg-white text-blue-600 hover:bg-gray-100 transition-colors duration-200" href="?page={{ page_obj.next_page_number }}{% if q %}&q={{q}}{% endif %}" aria-label="{% trans 'Next' %}"> <a class="page-link px-3 py-2 border border-gray-300 bg-white text-blue-600 hover:bg-gray-100 transition-colors duration-200" href="?page={{ page_obj.next_page_number }}{% if q %}&q={{q}}{% endif %}" aria-label="{% trans 'Next' %}">
<span class="fas fa-chevron-right" aria-hidden="true"></span> <span class="fas fa-chevron-right" aria-hidden="true"></span>
</a> </a>
</li> </li>
{% else %} {% else %}
<li class="page-item disabled rounded-md overflow-hidden"> <li class="page-item disabled rounded-md overflow-hidden">
<span class="page-link px-3 py-2 border border-gray-200 bg-gray-50 text-gray-400 cursor-not-allowed"> <span class="page-link px-3 py-2 border border-gray-200 bg-gray-50 text-gray-400 cursor-not-allowed">
<span class="fas fa-chevron-right" aria-hidden="true"></span> <span class="fas fa-chevron-right" aria-hidden="true"></span>
</span> </span>
</li> </li>
{% endif %} {% endif %}
{# Last Page Link #} {# Last Page Link #}
{% if page_obj.has_next %} {% if page_obj.has_next %}
<li class="page-item rounded-md overflow-hidden"> <li class="page-item rounded-md overflow-hidden">
<a class="page-link px-3 py-2 border border-gray-300 bg-white text-blue-600 hover:bg-gray-100 transition-colors duration-200" href="?page={{ page_obj.paginator.num_pages }}{% if q %}&q={{q}}{% endif %}" aria-label="{% trans 'Last' %}"> <a class="page-link px-3 py-2 border border-gray-300 bg-white text-blue-600 hover:bg-gray-100 transition-colors duration-200" href="?page={{ page_obj.paginator.num_pages }}{% if q %}&q={{q}}{% endif %}" aria-label="{% trans 'Last' %}">
<span class="fas fa-angle-double-right" aria-hidden="true"></span> <span class="fas fa-angle-double-right" aria-hidden="true"></span>
</a> </a>
</li> </li>
{% else %} {% else %}
<li class="page-item disabled rounded-md overflow-hidden"> <li class="page-item disabled rounded-md overflow-hidden">
<span class="page-link px-3 py-2 border border-gray-200 bg-gray-50 text-gray-400 cursor-not-allowed"> <span class="page-link px-3 py-2 border border-gray-200 bg-gray-50 text-gray-400 cursor-not-allowed">
<span class="fas fa-angle-double-right" aria-hidden="true"></span> <span class="fas fa-angle-double-right" aria-hidden="true"></span>
</span> </span>
</li> </li>
{% endif %} {% endif %}
</ul> </ul>
</nav> </nav>
</div> </div>

View File

@ -15,7 +15,7 @@
<button type="submit" class="btn btn-sm btn-phoenix-success me-2"> <button type="submit" class="btn btn-sm btn-phoenix-success me-2">
<i class="fa fa-save me-1"></i>{{ _("Save") }} <i class="fa fa-save me-1"></i>{{ _("Save") }}
</button> </button>
{% if object %} {% if object %}
<a class="btn btn-sm btn-phoenix-danger " href="{% url 'billing_info_delete' %}"><i class="fa-solid fa-trash me-1"></i> {{ _("Delete") }}</a> <a class="btn btn-sm btn-phoenix-danger " href="{% url 'billing_info_delete' %}"><i class="fa-solid fa-trash me-1"></i> {{ _("Delete") }}</a>
{% endif %} {% endif %}

View File

@ -50,29 +50,29 @@
</div> </div>
</div> </div>
</div> </div>
</div>
</div> </div>
</div>
<div class="col-lg-12"> <div class="col-lg-12">
<!-- PO Details --> <!-- PO Details -->
<div class="card"> <div class="card">
<div class="card-body"> <div class="card-body">
<h3 class="h4 fw-light mb-2">{{ po_model.po_title }}</h3> <h3 class="h4 fw-light mb-2">{{ po_model.po_title }}</h3>
<!-- PO Items Table --> <!-- PO Items Table -->
<div class="table-responsive"> <div class="table-responsive">
{% po_item_table1 po_items %} {% po_item_table1 po_items %}
</div>
</div> </div>
</div> </div>
<!--POS list-->
<a class="btn btn-phoenix-primary w-100 py-2 mt-2"
href="{% url 'purchase_order_list' %}">
<i class="fas fa-list me-2"></i>{% trans 'PO List' %}
</a>
</div> </div>
<!--POS list-->
<a class="btn btn-phoenix-primary w-100 py-2 mt-2"
href="{% url 'purchase_order_list' %}">
<i class="fas fa-list me-2"></i>{% trans 'PO List' %}
</a>
</div> </div>
</div> </div>
</div>
{% include "purchase_orders/includes/mark_as.html" %} {% include "purchase_orders/includes/mark_as.html" %}
{% endblock %} {% endblock %}

View File

@ -82,16 +82,16 @@
</tbody> </tbody>
</table> </table>
</div> </div>
{% if page_obj.paginator.num_pages > 1 %} {% if page_obj.paginator.num_pages > 1 %}
<div class="d-flex justify-content-end mt-3"> <div class="d-flex justify-content-end mt-3">
<div class="d-flex">
{% include 'partials/pagination.html'%}
</div>
<div class="d-flex">
{% include 'partials/pagination.html'%}
</div> </div>
{% endif %}
</div>
{% endif %}
</div> </div>
{% include 'modal/delete_modal.html' %} {% include 'modal/delete_modal.html' %}
{% endblock %} {% endblock %}

View File

@ -58,13 +58,13 @@
</div> </div>
{% if page_obj.paginator.num_pages > 1 %} {% if page_obj.paginator.num_pages > 1 %}
<div class="d-flex justify-content-end mt-3"> <div class="d-flex justify-content-end mt-3">
<div class="d-flex"> <div class="d-flex">
{% include 'partials/pagination.html'%} {% include 'partials/pagination.html'%}
</div> </div>
</div> </div>
{% endif %} {% endif %}
<div> <div>
{% endblock content %} {% endblock content %}

View File

@ -105,40 +105,40 @@
</div> </div>
</div> </div>
<div class="container bg-body dark__bg-gray-1100 p-4 mb-4 rounded-2"> <div class="container bg-body dark__bg-gray-1100 p-4 mb-4 rounded-2">
<div class="row"> <div class="row">
<div class="col mb-2"> <div class="col mb-2">
<h6><i class="fa-solid fa-hashtag"></i> {% trans 'Quotation Number' %}:</h6> <h6><i class="fa-solid fa-hashtag"></i> {% trans 'Quotation Number' %}:</h6>
<p class="fs-9 text-body-secondary fw-semibold mb-0">{{estimate.estimate_number}}</p> <p class="fs-9 text-body-secondary fw-semibold mb-0">{{estimate.estimate_number}}</p>
</div>
<div class="col mb-2">
<h6><i class="fa-solid fa-calendar-days"></i> {% trans 'Quotation Date' %}:</h6>
<p class="fs-9 text-body-secondary fw-semibold mb-0">{{estimate.created}}</p>
</div>
<div class="col mb-2">
<h6><i class="fa-solid fa-user"></i> {% trans 'Customer' %}:</h6>
<p class="fs-9 text-body-secondary fw-semibold mb-0">{{estimate.customer.customer_name}}</p>
</div>
<div class="col mb-2">
<h6><i class="fa-solid fa-envelope"></i> {% trans 'Email' %}:</h6>
<p class="fs-9 text-body-secondary fw-semibold mb-0">{{estimate.customer.email}}</p>
</div>
<div class="col">
<h6><i class="fa-solid fa-list"></i> {% trans "Quotation Status" %}:</h6>
<div class="fs-9 text-body-secondary fw-semibold mb-0">
{% if estimate.status == 'draft' %}
<span class="badge text-bg-warning">{% trans "Draft" %}</span>
{% elif estimate.status == 'in_review' %}
<span class="badge text-bg-info">{% trans "In Review" %}</span>
{% elif estimate.status == 'approved' %}
<span class="badge text-bg-success">{% trans "Approved" %}</span>
{% elif estimate.status == 'completed' %}
<span class="badge text-bg-success">{% trans "Completed" %}</span>
{% elif estimate.status == 'canceled' %}
<span class="badge text-bg-danger">{% trans "Canceled" %}</span>
{% endif %}
</div>
</div>
</div> </div>
<div class="col mb-2">
<h6><i class="fa-solid fa-calendar-days"></i> {% trans 'Quotation Date' %}:</h6>
<p class="fs-9 text-body-secondary fw-semibold mb-0">{{estimate.created}}</p>
</div>
<div class="col mb-2">
<h6><i class="fa-solid fa-user"></i> {% trans 'Customer' %}:</h6>
<p class="fs-9 text-body-secondary fw-semibold mb-0">{{estimate.customer.customer_name}}</p>
</div>
<div class="col mb-2">
<h6><i class="fa-solid fa-envelope"></i> {% trans 'Email' %}:</h6>
<p class="fs-9 text-body-secondary fw-semibold mb-0">{{estimate.customer.email}}</p>
</div>
<div class="col">
<h6><i class="fa-solid fa-list"></i> {% trans "Quotation Status" %}:</h6>
<div class="fs-9 text-body-secondary fw-semibold mb-0">
{% if estimate.status == 'draft' %}
<span class="badge text-bg-warning">{% trans "Draft" %}</span>
{% elif estimate.status == 'in_review' %}
<span class="badge text-bg-info">{% trans "In Review" %}</span>
{% elif estimate.status == 'approved' %}
<span class="badge text-bg-success">{% trans "Approved" %}</span>
{% elif estimate.status == 'completed' %}
<span class="badge text-bg-success">{% trans "Completed" %}</span>
{% elif estimate.status == 'canceled' %}
<span class="badge text-bg-danger">{% trans "Canceled" %}</span>
{% endif %}
</div>
</div>
</div>
</div> </div>
<div class="px-0"> <div class="px-0">

View File

@ -60,11 +60,11 @@
</table> </table>
</div> </div>
{% if page_obj.paginator.num_pages > 1 %} {% if page_obj.paginator.num_pages > 1 %}
<div class="d-flex justify-content-end mt-3"> <div class="d-flex justify-content-end mt-3">
<div class="d-flex"> <div class="d-flex">
{% include 'partials/pagination.html'%} {% include 'partials/pagination.html'%}
</div> </div>
</div> </div>
{% endif %} {% endif %}
</div> </div>
{% endblock %} {% endblock %}

View File

@ -172,41 +172,41 @@
<div class="container bg-body dark__bg-gray-1100 p-4 mb-4 rounded-2 text-body-tertiary {% if invoice.is_review %}disabled{% endif %}"> <div class="container bg-body dark__bg-gray-1100 p-4 mb-4 rounded-2 text-body-tertiary {% if invoice.is_review %}disabled{% endif %}">
<!----> <!---->
<div class="row"> <div class="row">
<div class="col mb-2"> <div class="col mb-2">
<h6 class="mb-0 me-3"><i class="fa-solid fa-hashtag"></i> {% trans "Invoice Number" %} :</h6> <h6 class="mb-0 me-3"><i class="fa-solid fa-hashtag"></i> {% trans "Invoice Number" %} :</h6>
<p class="fs-9 text-body-secondary fw-semibold mb-0">{{invoice.invoice_number}}</p> <p class="fs-9 text-body-secondary fw-semibold mb-0">{{invoice.invoice_number}}</p>
</div> </div>
<div class="col mb-2"> <div class="col mb-2">
<h6 class="me-3"><i class="fa-solid fa-calendar-days"></i> {% trans "Invoice Date" %} :</h6> <h6 class="me-3"><i class="fa-solid fa-calendar-days"></i> {% trans "Invoice Date" %} :</h6>
<p class="fs-9 text-body-secondary fw-semibold mb-0">{{invoice.created}}</p> <p class="fs-9 text-body-secondary fw-semibold mb-0">{{invoice.created}}</p>
</div> </div>
<div class="col mb-2"> <div class="col mb-2">
<h6 class="mb-2 me-3"><i class="fa-solid fa-user"></i> {% trans "Customer Name" %} :</h6> <h6 class="mb-2 me-3"><i class="fa-solid fa-user"></i> {% trans "Customer Name" %} :</h6>
<p class="fs-9 text-body-secondary fw-semibold mb-0">{{invoice.customer.customer_name}}</p> <p class="fs-9 text-body-secondary fw-semibold mb-0">{{invoice.customer.customer_name}}</p>
</div> </div>
<div class="col mb-2"> <div class="col mb-2">
<h6 class="mb-2"><i class="fa-solid fa-envelope"></i> {% trans "Customer Email" %} :</h6> <h6 class="mb-2"><i class="fa-solid fa-envelope"></i> {% trans "Customer Email" %} :</h6>
<p class="fs-9 text-body-secondary fw-semibold mb-0">{{invoice.customer.email}}</p> <p class="fs-9 text-body-secondary fw-semibold mb-0">{{invoice.customer.email}}</p>
</div> </div>
<div class="col"> <div class="col">
<h6 class="mb-2"><i class="fa-solid fa-list"></i> {% trans "Invoice Status" %} :</h6> <h6 class="mb-2"><i class="fa-solid fa-list"></i> {% trans "Invoice Status" %} :</h6>
<div class="fs-9 text-body-secondary fw-semibold mb-0"> <div class="fs-9 text-body-secondary fw-semibold mb-0">
{% if invoice.invoice_status == 'draft' %} {% if invoice.invoice_status == 'draft' %}
<span class="badge text-bg-warning">{% trans "Draft" %}</span> <span class="badge text-bg-warning">{% trans "Draft" %}</span>
{% elif invoice.invoice_status == 'in_review' %} {% elif invoice.invoice_status == 'in_review' %}
<span class="badge text-bg-info">{% trans "In Review" %}</span> <span class="badge text-bg-info">{% trans "In Review" %}</span>
{% elif invoice.invoice_status == 'approved' %} {% elif invoice.invoice_status == 'approved' %}
<span class="badge text-bg-info">{% trans "Approved" %}</span> <span class="badge text-bg-info">{% trans "Approved" %}</span>
{% elif invoice.invoice_status == 'declined' %} {% elif invoice.invoice_status == 'declined' %}
<span class="badge text-bg-danger">{% trans "Declined" %}</span> <span class="badge text-bg-danger">{% trans "Declined" %}</span>
{% elif invoice.invoice_status == 'paid' %} {% elif invoice.invoice_status == 'paid' %}
<span class="badge text-bg-success">{% trans "Paid" %}</span> <span class="badge text-bg-success">{% trans "Paid" %}</span>
{% endif %} {% endif %}
</div>
</div>
</div> </div>
</div>
</div>
</div> </div>
<div class="px-0 {% if invoice.is_review %}disabled{% endif %}"> <div class="px-0 {% if invoice.is_review %}disabled{% endif %}">
<div class="table-responsive scrollbar"> <div class="table-responsive scrollbar">

View File

@ -70,12 +70,12 @@
</tbody> </tbody>
</table> </table>
</div> </div>
{% if page_obj.paginator.num_pages > 1 %} {% if page_obj.paginator.num_pages > 1 %}
<div class="d-flex justify-content-end mt-3"> <div class="d-flex justify-content-end mt-3">
<div class="d-flex"> <div class="d-flex">
{% include 'partials/pagination.html'%} {% include 'partials/pagination.html'%}
</div> </div>
</div> </div>
{% endif %} {% endif %}
</div> </div>
{% endblock %} {% endblock %}

View File

@ -44,11 +44,11 @@
</table> </table>
</div> </div>
{% if page_obj.paginator.num_pages > 1%} {% if page_obj.paginator.num_pages > 1%}
<div class="d-flex justify-content-end mt-3"> <div class="d-flex justify-content-end mt-3">
<div class="d-flex"> <div class="d-flex">
{% include 'partials/pagination.html'%} {% include 'partials/pagination.html'%}
</div> </div>
</div> </div>
{% endif %} {% endif %}
</div> </div>
{% endblock %} {% endblock %}

View File

@ -53,11 +53,11 @@
</table> </table>
</div> </div>
{% if page_obj.paginator.num_pages > 1%} {% if page_obj.paginator.num_pages > 1%}
<div class="d-flex justify-content-end mt-3"> <div class="d-flex justify-content-end mt-3">
<div class="d-flex"> <div class="d-flex">
{% include 'partials/pagination.html'%} {% include 'partials/pagination.html'%}
</div> </div>
</div> </div>
{% endif %} {% endif %}
</div> </div>

View File

@ -54,11 +54,11 @@
</table> </table>
</div> </div>
{% if page_obj.paginator.num_pages > 1 %} {% if page_obj.paginator.num_pages > 1 %}
<div class="d-flex justify-content-end mt-3"> <div class="d-flex justify-content-end mt-3">
<div class="d-flex"> <div class="d-flex">
{% include 'partials/pagination.html'%} {% include 'partials/pagination.html'%}
</div> </div>
</div> </div>
{% endif %} {% endif %}
</div> </div>
{% endblock %} {% endblock %}

View File

@ -247,7 +247,7 @@
</div> </div>
<div class="card-footer text-end"> <div class="card-footer text-end">
<a href="{% url 'sales_list' %}" class="btn btn-phoenix-secondary"> <a href="{% url 'sales_list' request.dealer.slug %}" class="btn btn-phoenix-secondary">
{% trans "Back to List" %} {% trans "Back to List" %}
</a> </a>

View File

@ -204,7 +204,7 @@
<td class="align-middle text-end white-space-nowrap pe-0 action"> <td class="align-middle text-end white-space-nowrap pe-0 action">
<div class="btn-reveal-trigger position-static"> <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> <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 text-success-dark" href="{% url 'order_detail' tx.pk %}">{{ _("View Sales Order Detail") }}</a> <div class="dropdown-menu dropdown-menu-end py-2"><a class="dropdown-item text-success-dark" href="{% url 'order_detail' request.dealer.slug tx.pk %}">{{ _("View Sales Order Detail") }}</a>
</div> </div>
</div> </div>
</td> </td>