implement the dealer slug for inventory and sales

This commit is contained in:
ismail 2025-06-25 14:15:17 +03:00
parent 55d6e56233
commit 3ce318317e
55 changed files with 659 additions and 646 deletions

View File

@ -1,5 +1,7 @@
from django.conf import settings
from inventory.utils import get_user_type
def currency_context(request):
"""
@ -40,4 +42,4 @@ def breadcrumbs(request):
for i in range(len(path)):
url = "/" + "/".join(path[: i + 1]) + "/"
breadcrumbs.append({"name": path[i].capitalize(), "url": url})
return {"breadcrumbs": breadcrumbs}
return {"breadcrumbs": breadcrumbs}

View File

@ -1,6 +1,6 @@
from django.core.management.base import BaseCommand
from inventory.services import get_model, decodevin
from inventory.services import get_make, get_model, decodevin
from bs4 import BeautifulSoup
import requests
@ -24,35 +24,36 @@ class Command(BaseCommand):
)
self.stdout.write(self.style.SUCCESS(f"Generated VIN: {vin}"))
self.stdout.write(self.style.SUCCESS(f"Description: {description}"))
self.stdout.write(
self.style.SUCCESS(
"####################################################################################################"
)
)
self.stdout.write(
self.style.SUCCESS(
"####################################################################################################"
)
)
self.stdout.write(self.style.SUCCESS(f"Decoded VIN: {result}"))
make, model, year_model = result.values()
self.stdout.write(
self.style.SUCCESS(
f"VIN: {vin} - Make {make} - Model {model} - Model Year {year_model}"
)
)
m = get_model(model)
self.stdout.write(self.style.SUCCESS(f"Make: {m.id_car_make} - Model: {m}"))
self.stdout.write(
self.style.SUCCESS(
"####################################################################################################"
)
)
self.stdout.write(
self.style.SUCCESS(
"####################################################################################################"
)
)
# self.stdout.write(
# self.style.SUCCESS(
# "####################################################################################################"
# )
# )
# self.stdout.write(
# self.style.SUCCESS(
# "####################################################################################################"
# )
# )
# self.stdout.write(self.style.SUCCESS(f"Decoded VIN: {result}"))
# make, model, year_model = result.values()
# self.stdout.write(
# self.style.SUCCESS(
# f"VIN: {vin} - Make {make} - Model {model} - Model Year {year_model}"
# )
# )
# make = get_make(make)
# m = get_model(model,make)
# self.stdout.write(self.style.SUCCESS(f"Make: {m.id_car_make} - Model: {m}"))
# self.stdout.write(
# self.style.SUCCESS(
# "####################################################################################################"
# )
# )
# self.stdout.write(
# self.style.SUCCESS(
# "####################################################################################################"
# )
# )
def generate_vin(self):
# url = "https://www.vindecoder.org/vin-decoder"

View File

@ -1,4 +1,7 @@
import logging
from django.http import Http404, HttpResponseForbidden
from django.shortcuts import redirect
from inventory import models
from django.utils import timezone
@ -59,8 +62,11 @@ class InjectParamsMiddleware:
def __call__(self, request):
try:
# request.entity = request.user.dealer.entity
request.dealer = get_user_type(request)
if request.user.is_authenticated:
request.dealer = get_user_type(request)
request.entity = request.dealer.entity
else:
request.dealer = None
except Exception:
pass
response = self.get_response(request)
@ -107,3 +113,21 @@ class InjectDealerMiddleware:
# if request.user.is_authenticated and not request.session.get('otp_verified', False):
# return redirect(reverse('verify_otp'))
# return self.get_response(request)
class DealerSlugMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
response = self.get_response(request)
return response
def process_view(self, request, view_func, view_args, view_kwargs):
if request.user.is_authenticated:
dealer = get_user_type(request)
if 'dealer_slug' not in view_kwargs:
return redirect('home',
dealer_slug=dealer.slug,
**view_kwargs)
elif view_kwargs['dealer_slug'] != dealer.slug:
raise Http404("Dealer slug mismatch")
return None

View File

@ -0,0 +1,19 @@
# 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

@ -0,0 +1,17 @@
# 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

@ -1,5 +1,9 @@
from django.http import Http404
from django.shortcuts import redirect
from django.utils.translation import get_language
from inventory.utils import get_user_type
class AddClassMixin:
"""
@ -60,3 +64,15 @@ class LocalizedNameMixin:
# return super().form_valid(form)
# else:
# return form.errors
class DealerSlugMixin:
def dispatch(self, request, *args, **kwargs):
if request.user.is_authenticated:
if 'dealer_slug' not in kwargs:
return redirect('home',
dealer_slug=request.dealer.slug,
**kwargs)
elif kwargs['dealer_slug'] != request.dealer.slug:
raise Http404("Dealer slug mismatch")
return super().dispatch(request, *args, **kwargs)

View File

@ -442,6 +442,7 @@ def filter_by_role(accounts, role_prefix):
@register.inclusion_tag("purchase_orders/tags/po_item_table.html", takes_context=True)
def po_item_table1(context, queryset):
return {
"dealer_slug": context["view"].kwargs["dealer_slug"],
"entity_slug": context["entity_slug"],
"po_model": context["po_model"],
"po_item_list": queryset,
@ -459,6 +460,7 @@ def po_item_formset_table(context, po_model, itemtxs_formset):
item_role="inventory"
)
return {
"dealer_slug": context["view"].kwargs["dealer_slug"],
"entity_slug": context["view"].kwargs["entity_slug"],
"po_model": po_model,
"itemtxs_formset": itemtxs_formset,
@ -468,6 +470,7 @@ def po_item_formset_table(context, po_model, itemtxs_formset):
@register.inclusion_tag("bill/tags/bill_item_formset.html", takes_context=True)
def bill_item_formset_table(context, item_formset):
return {
"dealer_slug": context["view"].kwargs["dealer_slug"],
"entity_slug": context["view"].kwargs["entity_slug"],
"bill_pk": context["view"].kwargs["bill_pk"],
"total_amount__sum": context["total_amount__sum"],

View File

@ -1,13 +1,15 @@
from django.conf.urls import handler403, handler400, handler404, handler500
from django.urls import path
from django_tables2.export.export import TableExport
from inventory.utils import get_user_type
from . import views
from django.urls import path
from django.urls import reverse_lazy
from django.views.generic import RedirectView
from django_tables2.export.export import TableExport
from django.conf.urls import handler403, handler400, handler404, handler500
urlpatterns = [
# main URLs
path("", views.HomeView.as_view(), name="home"),
path("<slug:dealer_slug>/", views.HomeView.as_view(), name="home"),
path("welcome/", views.WelcomeView.as_view(), name="welcome"),
# Accounts URLs
# path("login/", allauth_views.LoginView.as_view(template_name="account/login.html"), name="account_login"),
@ -277,28 +279,28 @@ urlpatterns = [
name="vendor_delete",
),
# Car URLs
path("cars/upload_cars/", views.upload_cars, name="upload_cars"),
path("cars/<uuid:pk>/upload_cars/", views.upload_cars, name="upload_cars"),
path("cars/add/", views.CarCreateView.as_view(), name="car_add"),
path("cars/inventory/", views.CarInventory.as_view(), name="car_inventory_all"),
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/add/", views.CarCreateView.as_view(), name="car_add"),
path("<slug:dealer_slug>/cars/inventory/", views.CarInventory.as_view(), name="car_inventory_all"),
path(
"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(),
name="car_inventory",
),
path("cars/inventory/stats", views.inventory_stats_view, name="inventory_stats"),
path("cars/inventory/list", views.CarListView.as_view(), name="car_list"),
path("cars/<slug:slug>/", views.CarDetailView.as_view(), name="car_detail"),
path("<slug:dealer_slug>/cars/inventory/stats", 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>/update/", views.CarUpdateView.as_view(), name="car_update"),
path("cars/<slug:slug>/delete/", views.CarDeleteView.as_view(), name="car_delete"),
path("<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(
"cars/<slug:slug>/finance/create/",
"<slug:dealer_slug>/cars/<slug:slug>/finance/create/",
views.CarFinanceCreateView.as_view(),
name="car_finance_create",
),
path(
"cars/finance/<int:pk>/update/",
"<slug:dealer_slug>/cars/finance/<int:pk>/update/",
views.CarFinanceUpdateView.as_view(),
name="car_finance_update",
),
@ -307,12 +309,12 @@ urlpatterns = [
views.bulk_update_car_price,
name="bulk_update_car_price",
),
path("ajax/", views.AjaxHandlerView.as_view(), name="ajax_handler"),
path("<slug:dealer_slug>/ajax/", views.AjaxHandlerView.as_view(), name="ajax_handler"),
path(
"cars/<slug:slug>/add-color/", views.CarColorCreate.as_view(), name="add_color"
"<slug:dealer_slug>/cars/<slug:slug>/add-color/", views.CarColorCreate.as_view(), name="add_color"
),
path(
"car/colors/<slug:slug>/update/",
"<slug:dealer_slug>/car/colors/<slug:slug>/update/",
views.CarColorsUpdateView.as_view(),
name="car_colors_update",
),
@ -353,19 +355,19 @@ urlpatterns = [
),
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/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(
"reservations/<int:reservation_id>/",
"<slug:dealer_slug>/reservations/<int:reservation_id>/",
views.manage_reservation,
name="reservations",
),
path(
"cars/<slug:slug>/add-custom-card/",
"<slug:dealer_slug>/cars/<slug:slug>/add-custom-card/",
views.CustomCardCreateView.as_view(),
name="add_custom_card",
),
path(
"cars/<slug:slug>/add-registration/",
"<slug:dealer_slug>/cars/<slug:slug>/add-registration/",
views.CarRegistrationCreateView.as_view(),
name="add_registration",
),
@ -612,142 +614,146 @@ urlpatterns = [
),
# Bank Account
path(
"bank_accounts/", views.BankAccountListView.as_view(), name="bank_account_list"
"<slug:dealer_slug>/bank_accounts/", views.BankAccountListView.as_view(), name="bank_account_list"
),
path(
"bank_accounts/<uuid:pk>/",
"<slug:dealer_slug>/bank_accounts/<uuid:pk>/",
views.BankAccountDetailView.as_view(),
name="bank_account_detail",
),
path(
"bank_accounts/create/",
"<slug:dealer_slug>/bank_accounts/create/",
views.BankAccountCreateView.as_view(),
name="bank_account_create",
),
path(
"bank_accounts/<uuid:pk>/update/",
"<slug:dealer_slug>/bank_accounts/<uuid:pk>/update/",
views.BankAccountUpdateView.as_view(),
name="bank_account_update",
),
path(
"bank_accounts/<uuid:pk>/delete/",
"<slug:dealer_slug>/bank_accounts/<uuid:pk>/delete/",
views.bank_account_delete,
name="bank_account_delete",
),
# Account
path("coa_accounts/", views.AccountListView.as_view(), name="account_list"),
path("<slug:dealer_slug>/coa_accounts/", views.AccountListView.as_view(), name="account_list"),
path(
"coa_accounts/<uuid:pk>/",
"<slug:dealer_slug>/coa_accounts/<uuid:pk>/",
views.AccountDetailView.as_view(),
name="account_detail",
),
path(
"coa_accounts/create/", views.AccountCreateView.as_view(), name="account_create"
"<slug:dealer_slug>/coa_accounts/create/", views.AccountCreateView.as_view(), name="account_create"
),
path(
"coa_accounts/<uuid:pk>/update/",
"<slug:dealer_slug>/coa_accounts/<uuid:pk>/update/",
views.AccountUpdateView.as_view(),
name="account_update",
),
path("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
path("sales/estimates/", views.EstimateListView.as_view(), name="estimate_list"),
#################################################
path("<slug:dealer_slug>/sales/estimates/", views.EstimateListView.as_view(), name="estimate_list"),
path(
"sales/estimates/<uuid:pk>/",
"<slug:dealer_slug>/sales/estimates/<uuid:pk>/",
views.EstimateDetailView.as_view(),
name="estimate_detail",
),
path("sales/estimates/create/", views.create_estimate, name="estimate_create"),
path("<slug:dealer_slug>/sales/estimates/create/", views.create_estimate, name="estimate_create"),
path(
"sales/estimates/create/<slug:slug>/",
"<slug:dealer_slug>/sales/estimates/create/<slug:slug>/",
views.create_estimate,
name="estimate_create_from_opportunity",
),
path(
"sales/estimates/<uuid:pk>/estimate_mark_as/",
"<slug:dealer_slug>/sales/estimates/<uuid:pk>/estimate_mark_as/",
views.estimate_mark_as,
name="estimate_mark_as",
),
path(
"sales/estimates/<uuid:pk>/preview/",
"<slug:dealer_slug>/sales/estimates/<uuid:pk>/preview/",
views.EstimatePreviewView.as_view(),
name="estimate_preview",
),
path(
"sales/estimates/<uuid:pk>/payment_request/",
"<slug:dealer_slug>/sales/estimates/<uuid:pk>/payment_request/",
views.PaymentRequest.as_view(),
name="payment_request",
),
path(
"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(
"sales/estimates/<uuid:pk>/sale_order/",
"<slug:dealer_slug>/sales/estimates/<uuid:pk>/sale_order/",
views.create_sale_order,
name="create_sale_order",
),
path(
"sales/estimates/<uuid:pk>/sale_order/<int:order_pk>/details/",
"<slug:dealer_slug>/sales/estimates/<uuid:pk>/sale_order/<int:order_pk>/details/",
views.SaleOrderDetail.as_view(),
name="sale_order_details",
),
path(
"sales/estimates/<uuid:pk>/sale_order/preview/",
"<slug:dealer_slug>/sales/estimates/<uuid:pk>/sale_order/preview/",
views.preview_sale_order,
name="preview_sale_order",
),
###############################################
# Invoice
path("sales/invoices/", views.InvoiceListView.as_view(), name="invoice_list"),
###############################################
path("<slug:dealer_slug>/sales/invoices/", views.InvoiceListView.as_view(), name="invoice_list"),
path(
"sales/invoices/<uuid:pk>/create/", views.invoice_create, name="invoice_create"
"<slug:dealer_slug>/sales/invoices/<uuid:pk>/create/", views.invoice_create, name="invoice_create"
),
path(
"sales/invoices/<uuid:pk>/",
"<slug:dealer_slug>/sales/invoices/<uuid:pk>/",
views.InvoiceDetailView.as_view(),
name="invoice_detail",
),
path(
"sales/invoices/<uuid:pk>/preview/",
"<slug:dealer_slug>/sales/invoices/<uuid:pk>/preview/",
views.InvoicePreviewView.as_view(),
name="invoice_preview",
),
path(
"sales/invoices/<uuid:pk>/invoice_mark_as/",
"<slug:dealer_slug>/sales/invoices/<uuid:pk>/invoice_mark_as/",
views.invoice_mark_as,
name="invoice_mark_as",
),
path(
"sales/invoices/<uuid:pk>/draft_invoice_update/",
"<slug:dealer_slug>/sales/invoices/<uuid:pk>/draft_invoice_update/",
views.DraftInvoiceModelUpdateFormView.as_view(),
name="draft_invoice_update",
),
path(
"sales/invoices/<uuid:pk>/approved_invoice_update/",
"<slug:dealer_slug>/sales/invoices/<uuid:pk>/approved_invoice_update/",
views.ApprovedInvoiceModelUpdateFormView.as_view(),
name="approved_invoice_update",
),
path(
"sales/invoices/<uuid:pk>/paid_invoice_update/",
"<slug:dealer_slug>/sales/invoices/<uuid:pk>/paid_invoice_update/",
views.PaidInvoiceModelUpdateFormView.as_view(),
name="paid_invoice_update",
),
# path('sales/estimates/<uuid:pk>/preview/', views.EstimatePreviewView.as_view(), name='estimate_preview'),
# path('send_email/<uuid:pk>', views.send_email, name='send_email'),
# Payment
path("sales/payments/", views.PaymentListView, name="payment_list"),
path("<slug:dealer_slug>/sales/payments/", views.PaymentListView, name="payment_list"),
path(
"sales/payments/<uuid:pk>/create/",
"<slug:dealer_slug>/sales/payments/<uuid:pk>/create/",
views.PaymentCreateView,
name="payment_create",
),
# path("sales/payments/create/", views.PaymentCreateView, name="payment_create"),
path(
"sales/payments/<uuid:pk>/payment_details/",
"<slug:dealer_slug>/sales/payments/<uuid:pk>/payment_details/",
views.PaymentDetailView,
name="payment_details",
),
path(
"sales/payments/<uuid:pk>/payment_mark_as_paid/",
"<slug:dealer_slug>/sales/payments/<uuid:pk>/payment_mark_as_paid/",
views.payment_mark_as_paid,
name="payment_mark_as_paid",
),
@ -787,85 +793,86 @@ urlpatterns = [
name="item_expense_update",
),
# Bills
path("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/<slug:entity_slug>/create/",
"<slug:dealer_slug>/items/bills/<slug:entity_slug>/create/",
views.BillModelCreateView.as_view(),
name="bill-create",
),
path(
"items/bills/<slug:entity_slug>/create/purchase-order/<uuid:po_pk>/",
"<slug:dealer_slug>/items/bills/<slug:entity_slug>/create/purchase-order/<uuid:po_pk>/",
views.BillModelCreateView.as_view(for_purchase_order=True),
name="bill-create-po",
),
path(
"items/bills/<slug:entity_slug>/create/estimate/<uuid:ce_pk>/",
"<slug:dealer_slug>/items/bills/<slug:entity_slug>/create/estimate/<uuid:ce_pk>/",
views.BillModelCreateView.as_view(for_estimate=True),
name="bill-create-estimate",
),
path(
"items/bills/<slug:entity_slug>/detail/<uuid:bill_pk>/",
"<slug:dealer_slug>/items/bills/<slug:entity_slug>/detail/<uuid:bill_pk>/",
views.BillModelDetailViewView.as_view(),
name="bill-detail",
),
path(
"items/bills/<slug:entity_slug>/update/<uuid:bill_pk>/",
"<slug:dealer_slug>/items/bills/<slug:entity_slug>/update/<uuid:bill_pk>/",
views.BillModelUpdateViewView.as_view(),
name="bill-update",
),
path(
"items/bills/<slug:entity_slug>/update/<uuid:bill_pk>/items/",
"<slug:dealer_slug>/items/bills/<slug:entity_slug>/update/<uuid:bill_pk>/items/",
views.BillModelUpdateViewView.as_view(action_update_items=True),
name="bill-update-items",
),
############################################################
path(
"items/bills/<slug:entity_slug>/actions/<uuid:bill_pk>/mark-as-draft/",
"<slug:dealer_slug>/items/bills/<slug:entity_slug>/actions/<uuid:bill_pk>/mark-as-draft/",
views.BillModelActionMarkAsDraftView.as_view(),
name="bill-action-mark-as-draft",
),
path(
"items/bills/<slug:entity_slug>/actions/<uuid:bill_pk>/mark-as-review/",
"<slug:dealer_slug>/items/bills/<slug:entity_slug>/actions/<uuid:bill_pk>/mark-as-review/",
views.BillModelActionMarkAsInReviewView.as_view(),
name="bill-action-mark-as-review",
),
path(
"items/bills/<slug:entity_slug>/actions/<uuid:bill_pk>/mark-as-approved/",
"<slug:dealer_slug>/items/bills/<slug:entity_slug>/actions/<uuid:bill_pk>/mark-as-approved/",
views.BillModelActionMarkAsApprovedView.as_view(),
name="bill-action-mark-as-approved",
),
path(
"items/bills/<slug:entity_slug>/actions/<uuid:bill_pk>/mark-as-paid/",
"<slug:dealer_slug>/items/bills/<slug:entity_slug>/actions/<uuid:bill_pk>/mark-as-paid/",
views.BillModelActionMarkAsPaidView.as_view(),
name="bill-action-mark-as-paid",
),
path(
"items/bills/<slug:entity_slug>/actions/<uuid:bill_pk>/mark-as-void/",
"<slug:dealer_slug>/items/bills/<slug:entity_slug>/actions/<uuid:bill_pk>/mark-as-void/",
views.BillModelActionVoidView.as_view(),
name="bill-action-mark-as-void",
),
path(
"items/bills/<slug:entity_slug>/actions/<uuid:bill_pk>/mark-as-canceled/",
"<slug:dealer_slug>/items/bills/<slug:entity_slug>/actions/<uuid:bill_pk>/mark-as-canceled/",
views.BillModelActionCanceledView.as_view(),
name="bill-action-mark-as-canceled",
),
path(
"items/bills/<slug:entity_slug>/actions/<uuid:bill_pk>/lock-ledger/",
"<slug:dealer_slug>/items/bills/<slug:entity_slug>/actions/<uuid:bill_pk>/lock-ledger/",
views.BillModelActionLockLedgerView.as_view(),
name="bill-action-lock-ledger",
),
path(
"items/bills/<slug:entity_slug>/actions/<uuid:bill_pk>/unlock-ledger/",
"<slug:dealer_slug>/items/bills/<slug:entity_slug>/actions/<uuid:bill_pk>/unlock-ledger/",
views.BillModelActionUnlockLedgerView.as_view(),
name="bill-action-unlock-ledger",
),
path(
"items/bills/<slug:entity_slug>/actions/<uuid:bill_pk>/force-migration/",
"<slug:dealer_slug>/items/bills/<slug:entity_slug>/actions/<uuid:bill_pk>/force-migration/",
views.BillModelActionForceMigrateView.as_view(),
name="bill-action-force-migrate",
),
# path("items/bills/create/", views.bill_create, name="bill_create"),
# >>>>>>>>>>>>>>>>>>>>>>...
path(
"items/bills/<uuid:pk>/bill_detail/",
views.BillDetailView.as_view(),
@ -1039,78 +1046,78 @@ urlpatterns = [
#########
# Purchase Order
path(
"purchase_orders/",
"<slug:dealer_slug>/purchase_orders/",
views.PurchaseOrderListView.as_view(),
name="purchase_order_list",
),
path(
"purchase_orders/new/",
"<slug:dealer_slug>/purchase_orders/new/",
views.PurchaseOrderCreateView,
name="purchase_order_create",
),
path(
"purchase_orders/<uuid:pk>/detail/",
"<slug:dealer_slug>/purchase_orders/<uuid:pk>/detail/",
views.PurchaseOrderDetailView.as_view(),
name="purchase_order_detail",
),
path(
"purchase_orders/<slug:entity_slug>/<uuid:po_pk>/update/",
"<slug:dealer_slug>/purchase_orders/<slug:entity_slug>/<uuid:po_pk>/update/",
views.PurchaseOrderUpdateView.as_view(),
name="purchase_order_update",
),
path(
"purchase_orders/<slug:entity_slug>/update/<uuid:po_pk>/update-items/",
"<slug:dealer_slug>/purchase_orders/<slug:entity_slug>/update/<uuid:po_pk>/update-items/",
views.PurchaseOrderUpdateView.as_view(action_update_items=True),
name="purchase_order_update_items",
),
path(
"purchase_orders/inventory_item/create/",
"<slug:dealer_slug>/purchase_orders/inventory_item/create/",
views.InventoryItemCreateView,
name="inventory_item_create",
),
path(
"purchase_orders/inventory_items_filter/",
"<slug:dealer_slug>/purchase_orders/inventory_items_filter/",
views.inventory_items_filter,
name="inventory_items_filter",
),
path(
"purchase_orders/<slug:entity_slug>/delete/<uuid:po_pk>/",
"<slug:dealer_slug>/purchase_orders/<slug:entity_slug>/delete/<uuid:po_pk>/",
views.PurchaseOrderModelDeleteView.as_view(),
name="po-delete",
),
path(
"purchase_orders/<slug:entity_slug>/<uuid:po_pk>/upload/",
"<slug:dealer_slug>/purchase_orders/<slug:entity_slug>/<uuid:po_pk>/upload/",
view=views.view_items_inventory,
name="view_items_inventory",
),
# Actions....
path(
"<slug:entity_slug>/action/<uuid:po_pk>/mark-as-draft/",
"<slug:dealer_slug>/<slug:entity_slug>/action/<uuid:po_pk>/mark-as-draft/",
views.PurchaseOrderMarkAsDraftView.as_view(),
name="po-action-mark-as-draft",
),
path(
"<slug:entity_slug>/action/<uuid:po_pk>/mark-as-review/",
"<slug:dealer_slug>/<slug:entity_slug>/action/<uuid:po_pk>/mark-as-review/",
views.PurchaseOrderMarkAsReviewView.as_view(),
name="po-action-mark-as-review",
),
path(
"<slug:entity_slug>/action/<uuid:po_pk>/mark-as-approved/",
"<slug:dealer_slug>/<slug:entity_slug>/action/<uuid:po_pk>/mark-as-approved/",
views.PurchaseOrderMarkAsApprovedView.as_view(),
name="po-action-mark-as-approved",
),
path(
"<slug:entity_slug>/action/<uuid:po_pk>/mark-as-fulfilled/",
"<slug:dealer_slug>/<slug:entity_slug>/action/<uuid:po_pk>/mark-as-fulfilled/",
views.PurchaseOrderMarkAsFulfilledView.as_view(),
name="po-action-mark-as-fulfilled",
),
path(
"<slug:entity_slug>/action/<uuid:po_pk>/mark-as-canceled/",
"<slug:dealer_slug>/<slug:entity_slug>/action/<uuid:po_pk>/mark-as-canceled/",
views.PurchaseOrderMarkAsCanceledView.as_view(),
name="po-action-mark-as-canceled",
),
path(
"<slug:entity_slug>/action/<uuid:po_pk>/mark-as-void/",
"<slug:dealer_slug>/<slug:entity_slug>/action/<uuid:po_pk>/mark-as-void/",
views.PurchaseOrderMarkAsVoidView.as_view(),
name="po-action-mark-as-void",
),

View File

@ -222,7 +222,7 @@ def reserve_car(car, request):
except Exception as e:
messages.error(request, f"Error reserving car: {e}")
return redirect("car_detail", slug=car.slug)
return redirect("car_detail", dealer_slug=request.dealer.slug, slug=car.slug)
def calculate_vat_amount(amount):
@ -1011,7 +1011,7 @@ class CarFinanceCalculator:
"make": car_info.get("make"),
"model": car_info.get("model"),
"year": car_info.get("year"),
"logo": item.item_model.car.id_car_make.logo.url,
"logo": getattr(item.item_model.car.id_car_make, "logo", ""),
"trim": car_info.get("trim"),
"mileage": car_info.get("mileage"),
"cost_price": car_finance.get("cost_price"),

View File

@ -20,6 +20,7 @@ from pyzbar.pyzbar import decode
from urllib.parse import urlparse, urlunparse
#####################################################################
from inventory.mixins import DealerSlugMixin
from inventory.models import Status as LeadStatus
from django.db import IntegrityError
from background_task.models import Task
@ -348,10 +349,13 @@ class HomeView(LoginRequiredMixin, TemplateView):
template_name = "index.html"
def dispatch(self, request, *args, **kwargs):
def dispatch(self, request,*args, **kwargs):
# Redirect unauthenticated users to the welcome page
if not request.user.is_authenticated:
return redirect("welcome")
if not kwargs.get("dealer_slug"):
dealer = get_user_type(request)
return redirect("home", dealer_slug=dealer.slug)
return super().dispatch(request, *args, **kwargs)
@ -616,8 +620,8 @@ class CarCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView):
def get_success_url(self):
"""Determine the redirect URL based on user choice."""
if self.request.POST.get("add_another"):
return reverse("car_add")
return reverse("inventory_stats")
return reverse("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):
dealer = get_user_type(self.request)
@ -628,9 +632,7 @@ class CarCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView):
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
dealer = get_user_type(self.request)
context["vendor_exists"] = dealer.vendors.exists()
context["vendor_exists"] = self.request.dealer.vendors.exists()
return context
@ -673,7 +675,7 @@ class AjaxHandlerView(LoginRequiredMixin, View):
:ivar request: Django request object containing HTTP request details.
"""
def get(self, request, *args, **kwargs):
def get(self, request,dealer_slug, *args, **kwargs):
action = request.GET.get("action")
handlers = {
"decode_vin": self.decode_vin,
@ -986,7 +988,7 @@ class CarColorCreate(LoginRequiredMixin, PermissionRequiredMixin, CreateView):
return super().form_valid(form)
def get_success_url(self):
return reverse_lazy("car_detail", kwargs={"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):
context = super().get_context_data(**kwargs)
@ -1025,7 +1027,7 @@ class CarColorsUpdateView(
"""
# self.object refers to the CarColors instance that was just updated.
# self.object.car then refers to the associated Car instance.
return reverse("car_detail", kwargs={"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):
"""
@ -1138,7 +1140,7 @@ class CarListView(LoginRequiredMixin, PermissionRequiredMixin, ListView):
@login_required
def inventory_stats_view(request):
def inventory_stats_view(request,dealer_slug):
"""
Handle the inventory stats view for a dealer, displaying detailed information
about the cars, including counts grouped by make, model, and trim.
@ -1154,10 +1156,9 @@ def inventory_stats_view(request):
"inventory/inventory_stats.html" template.
:rtype: HttpResponse
"""
dealer = get_user_type(request)
# Base queryset for cars belonging to the dealer
cars = models.Car.objects.filter(dealer=dealer)
cars = models.Car.objects.filter(dealer=request.dealer)
# Count for total, reserved, showroom, and unreserved cars
total_cars = cars.count()
@ -1241,7 +1242,6 @@ def inventory_stats_view(request):
for make_data in inventory.values()
],
}
print(result["makes"])
return render(request, "inventory/inventory_stats.html", {"inventory": result})
@ -1427,7 +1427,7 @@ class CarFinanceCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateVi
return super().form_valid(form)
def get_success_url(self):
return reverse("car_detail", kwargs={"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):
context = super().get_context_data(**kwargs)
@ -1474,7 +1474,7 @@ class CarFinanceUpdateView(
permission_required = ["inventory.change_carfinance"]
def get_success_url(self):
return reverse("car_detail", kwargs={"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):
kwargs = super().get_form_kwargs()
@ -1871,7 +1871,7 @@ class CustomCardCreateView(LoginRequiredMixin, CreateView):
def get_success_url(self):
messages.success(self.request, _("Custom Card added successfully"))
return reverse_lazy("car_detail", kwargs={"slug": self.kwargs["slug"]})
return reverse_lazy("car_detail", kwargs={"dealer_slug":self.request.dealer.slug,"slug": self.kwargs["slug"]})
class CarRegistrationCreateView(LoginRequiredMixin, CreateView):
@ -1913,11 +1913,11 @@ class CarRegistrationCreateView(LoginRequiredMixin, CreateView):
def get_success_url(self):
messages.success(self.request, _("Registration added successfully"))
return reverse_lazy("car_detail", kwargs={"slug": self.kwargs["slug"]})
return reverse_lazy("car_detail", kwargs={"dealer_slug":self.request.dealer.slug,"slug": self.kwargs["slug"]})
@login_required()
def reserve_car_view(request, slug):
def reserve_car_view(request, dealer_slug,slug):
"""
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
@ -1945,7 +1945,7 @@ def reserve_car_view(request, slug):
@login_required
def manage_reservation(request, reservation_id):
def manage_reservation(request,dealer_slug, reservation_id):
"""
Handles the management of a car reservation, providing options to renew or
cancel an existing reservation associated with the logged-in user.
@ -1974,7 +1974,7 @@ def manage_reservation(request, reservation_id):
reservation.reserved_until = timezone.now() + timezone.timedelta(hours=24)
reservation.save()
messages.success(request, _("Reservation renewed successfully"))
return redirect("car_detail", slug=reservation.car.slug)
return redirect("car_detail",dealer_slug=request.dealer.slug, slug=reservation.car.slug)
elif action == "cancel":
car = reservation.car
@ -1982,7 +1982,7 @@ def manage_reservation(request, reservation_id):
car.status = models.CarStatusChoices.AVAILABLE
car.save()
messages.success(request, _("Reservation canceled successfully"))
return redirect("car_detail", slug=reservation.car.slug)
return redirect("car_detail",dealer_slug=request.dealer.slug, slug=reservation.car.slug)
else:
return JsonResponse(
@ -3444,21 +3444,20 @@ class BankAccountUpdateView(
model = BankAccountModel
form_class = BankAccountUpdateForm
template_name = "ledger/bank_accounts/bank_account_form.html"
success_url = reverse_lazy("bank_account_list")
success_message = _("Bank account updated successfully")
permission_required = ["inventory.view_carfinance"]
def get_form_kwargs(self):
dealer = get_user_type(self.request)
dealer = get_object_or_404(models.Dealer, slug=self.kwargs["dealer_slug"])
entity = dealer.entity
kwargs = super().get_form_kwargs()
kwargs["entity_slug"] = entity.slug # Get entity_slug from URL
kwargs["user_model"] = entity.admin # Get user_model from the request
return kwargs
def get_success_url(self):
return reverse_lazy("bank_account_detail", kwargs={"dealer_slug": self.kwargs["dealer_slug"], "pk": self.object.pk})
@login_required
def bank_account_delete(request, pk):
def bank_account_delete(request,dealer_slug, pk):
"""
Delete a bank account entry from the database.
@ -3478,11 +3477,12 @@ def bank_account_delete(request, pk):
rendering the confirmation template if accessed via GET.
:rtype: HttpResponse
"""
dealer = get_object_or_404(models.Dealer, slug=dealer_slug)
bank_account = get_object_or_404(BankAccountModel, pk=pk)
if request.method == "POST":
bank_account.delete()
messages.success(request, _("Bank account deleted successfully"))
return redirect("bank_account_list")
return redirect("bank_account_list", dealer_slug=dealer_slug)
return render(
request,
"ledger/bank_accounts/bank_account_delete.html",
@ -3568,7 +3568,6 @@ class AccountCreateView(
model = AccountModel
form_class = AccountModelCreateForm
template_name = "ledger/coa_accounts/account_form.html"
success_url = reverse_lazy("account_list")
success_message = _("Account created successfully")
permission_required = ["inventory.view_carfinance"]
@ -3591,6 +3590,8 @@ class AccountCreateView(
entity = get_user_type(self.request).entity
form.initial["coa_model"] = entity.get_default_coa()
return form
def get_success_url(self):
return reverse("account_list", kwargs={"dealer_slug":self.kwargs["dealer_slug"]})
class AccountDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailView):
@ -3682,7 +3683,6 @@ class AccountUpdateView(
model = AccountModel
form_class = AccountModelUpdateForm
template_name = "ledger/coa_accounts/account_form.html"
success_url = reverse_lazy("account_list")
success_message = _("Account updated successfully")
permission_required = ["inventory.view_carfinance"]
@ -3691,11 +3691,12 @@ class AccountUpdateView(
form.fields["_ref_node_id"].widget = HiddenInput()
form.fields["_position"].widget = HiddenInput()
return form
def get_success_url(self):
return reverse_lazy("account_list", kwargs={"dealer_slug":self.kwargs["dealer_slug"]})
@login_required
@permission_required("inventory.view_carfinance")
def account_delete(request, pk):
def account_delete(request, dealer_slug,pk):
"""
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
@ -3708,11 +3709,13 @@ def account_delete(request, pk):
:return: An HTTP redirect response to the account list page.
:rtype: HttpResponse
"""
dealer = get_object_or_404(models.Dealer, slug=dealer_slug)
account = get_object_or_404(AccountModel, pk=pk)
account.delete()
messages.success(request, _("Account deleted successfully"))
return redirect("account_list")
return redirect("account_list",dealer_slug=dealer_slug)
# Sales list
@ -3809,7 +3812,7 @@ class EstimateListView(LoginRequiredMixin, PermissionRequiredMixin, ListView):
# @csrf_exempt
@login_required
@permission_required("django_ledger.add_estimatemodel", raise_exception=True)
def create_estimate(request, 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
a POST request and expects a JSON payload containing details of the estimate such as
@ -3829,7 +3832,7 @@ def create_estimate(request, slug=None):
estimate creation form.
:rtype: JsonResponse or HttpResponse
"""
dealer = get_user_type(request)
dealer = get_object_or_404(models.Dealer, slug=dealer_slug)
entity = dealer.entity
if request.method == "POST":
@ -4002,7 +4005,7 @@ def create_estimate(request, slug=None):
opportunity.estimate = estimate
opportunity.save()
url = reverse("estimate_detail", kwargs={"pk": estimate.pk})
url = reverse("estimate_detail", kwargs={"dealer_slug": dealer.slug,"pk": estimate.pk})
return JsonResponse(
{
"status": "success",
@ -4108,7 +4111,7 @@ class EstimateDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailView
@login_required
@permission_required("inventory.add_saleorder", raise_exception=True)
def create_sale_order(request, pk):
def create_sale_order(request,dealer_slug, pk):
"""
Creates a sale order for a given estimate and updates associated item and car data.
@ -4127,7 +4130,7 @@ def create_sale_order(request, pk):
POST data, or redirects to the estimate detail view upon successful creation.
:rtype: HttpResponse
"""
dealer = get_user_type(request)
dealer = get_object_or_404(models.Dealer, slug=dealer_slug)
estimate = get_object_or_404(EstimateModel, pk=pk)
items = estimate.get_itemtxs_data()[0].all()
if request.method == "POST":
@ -4157,7 +4160,7 @@ def create_sale_order(request, pk):
else:
print(form.errors)
messages.error(request, "Invalid form data")
return redirect("estimate_detail", pk=estimate.pk)
return redirect("estimate_detail", dealer_slug=dealer_slug, pk=estimate.pk)
form = forms.SaleOrderForm()
customer = estimate.customer.customer_set.first()
@ -4201,7 +4204,7 @@ class SaleOrderDetail(DetailView):
@login_required
def preview_sale_order(request, pk):
def preview_sale_order(request, dealer_slug,pk):
"""
Handles rendering of the sale order preview page for a specific estimate.
@ -4216,6 +4219,7 @@ def preview_sale_order(request, pk):
:return: HTTP response containing the rendered sale order preview page
:rtype: HttpResponse
"""
dealer = get_object_or_404(models.Dealer, slug=dealer_slug)
estimate = get_object_or_404(EstimateModel, pk=pk)
data = get_car_finance_data(estimate)
return render(
@ -4289,19 +4293,18 @@ class EstimatePreviewView(LoginRequiredMixin, PermissionRequiredMixin, DetailVie
permission_required = ["django_ledger.view_estimatemodel"]
def get_context_data(self, **kwargs):
dealer = get_user_type(self.request)
estimate = kwargs.get("object")
if estimate.get_itemtxs_data():
# data = get_financial_values(estimate)
calculator = CarFinanceCalculator(estimate)
kwargs["data"] = calculator.get_finance_data()
kwargs["dealer"] = dealer
return super().get_context_data(**kwargs)
@login_required
@permission_required("django_ledger.change_estimatemodel", raise_exception=True)
def estimate_mark_as(request, pk):
def estimate_mark_as(request,dealer_slug, pk):
"""
Marks an estimate with a specified status based on the requested action and
permissions. The marking possibilities include review, approval, rejection,
@ -4317,35 +4320,36 @@ def estimate_mark_as(request, pk):
:return: A redirect response to the estimate detail view.
:rtype: HttpResponseRedirect
"""
dealer = get_object_or_404(models.Dealer, slug=dealer_slug)
estimate = get_object_or_404(EstimateModel, pk=pk)
mark = request.GET.get("mark")
if mark:
if mark == "review":
if not estimate.can_review():
messages.error(request, _("Quotation is not ready for review"))
return redirect("estimate_detail", pk=estimate.pk)
return redirect("estimate_detail",dealer_slug=dealer.slug, pk=estimate.pk)
estimate.mark_as_review()
elif mark == "approved":
if not estimate.can_approve():
messages.error(request, _("Quotation is not ready for approval"))
return redirect("estimate_detail", pk=estimate.pk)
return redirect("estimate_detail",dealer_slug=dealer.slug, pk=estimate.pk)
estimate.mark_as_approved()
messages.success(request, _("Quotation approved successfully"))
elif mark == "rejected":
if not estimate.can_cancel():
messages.error(request, _("Quotation is not ready for rejection"))
return redirect("estimate_detail", pk=estimate.pk)
return redirect("estimate_detail",dealer_slug=dealer.slug, pk=estimate.pk)
estimate.mark_as_canceled()
messages.success(request, _("Quotation canceled successfully"))
elif mark == "completed":
if not estimate.can_complete():
messages.error(request, _("Quotation is not ready for completion"))
return redirect("estimate_detail", pk=estimate.pk)
return redirect("estimate_detail",dealer_slug=dealer.slug, pk=estimate.pk)
elif mark == "canceled":
if not estimate.can_cancel():
messages.error(request, _("Quotation is not ready for cancellation"))
return redirect("estimate_detail", pk=estimate.pk)
return redirect("estimate_detail", dealer_slug=dealer.slug, pk=estimate.pk)
estimate.mark_as_canceled()
try:
car = models.Car.objects.get(
@ -4359,7 +4363,7 @@ def estimate_mark_as(request, pk):
estimate.save()
messages.success(request, _("Quotation marked as ") + mark.upper())
return redirect("estimate_detail", pk=estimate.pk)
return redirect("estimate_detail", dealer_slug=dealer.slug, pk=estimate.pk)
# Invoice
@ -4392,8 +4396,8 @@ class InvoiceListView(LoginRequiredMixin, PermissionRequiredMixin, ListView):
permission_required = ["django_ledger.view_invoicemodel"]
def get_queryset(self):
dealer = get_object_or_404(models.Dealer, slug=self.kwargs["dealer_slug"])
query = self.request.GET.get("q")
dealer = get_user_type(self.request)
invoices = dealer.entity.get_invoices()
return apply_search_filters(invoices, query)
@ -4474,7 +4478,7 @@ class DraftInvoiceModelUpdateFormView(
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
dealer = get_user_type(self.request)
dealer = get_object_or_404(models.Dealer, slug=self.kwargs["dealer_slug"])
kwargs["entity_slug"] = dealer.entity
kwargs["user_model"] = dealer.entity.admin
return kwargs
@ -4516,13 +4520,13 @@ class ApprovedInvoiceModelUpdateFormView(
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
dealer = get_user_type(self.request)
dealer = get_object_or_404(models.Dealer, slug=self.kwargs["dealer_slug"])
kwargs["entity_slug"] = dealer.entity
kwargs["user_model"] = dealer.entity.admin
return kwargs
def get_success_url(self):
return reverse_lazy("invoice_detail", kwargs={"pk": self.object.pk})
return reverse_lazy("invoice_detail", kwargs={"dealer_slug":self.kwargs["dealer_slug"],"pk": self.object.pk})
class PaidInvoiceModelUpdateFormView(
@ -4561,13 +4565,13 @@ class PaidInvoiceModelUpdateFormView(
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
dealer = get_user_type(self.request)
dealer = get_object_or_404(models.Dealer, slug=self.kwargs["dealer_slug"])
kwargs["entity_slug"] = dealer.entity
kwargs["user_model"] = dealer.entity.admin
return kwargs
def get_success_url(self):
return reverse_lazy("invoice_detail", kwargs={"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):
invoice = form.save()
@ -4583,7 +4587,7 @@ class PaidInvoiceModelUpdateFormView(
@login_required
@permission_required("django_ledger.change_invoicemodel", raise_exception=True)
def invoice_mark_as(request, pk):
def invoice_mark_as(request,dealer_slug, pk):
"""
Marks an invoice as approved if it meets the required conditions.
@ -4600,23 +4604,23 @@ def invoice_mark_as(request, pk):
:return: An HTTP redirect response to the invoice detail page after processing.
:rtype: django.http.HttpResponse
"""
dealer = get_object_or_404(models.Dealer, slug=dealer_slug)
invoice = get_object_or_404(InvoiceModel, pk=pk)
dealer = get_user_type(request)
mark = request.GET.get("mark")
if mark and mark == "accept":
if not invoice.can_approve():
messages.error(request, "invoice is not ready for approval")
return redirect("invoice_detail", pk=invoice.pk)
return redirect("invoice_detail",dealer_slug=dealer_slug, pk=invoice.pk)
invoice.mark_as_approved(
entity_slug=dealer.entity.slug, user_model=dealer.entity.admin
)
invoice.save()
return redirect("invoice_detail", pk=invoice.pk)
return redirect("invoice_detail",dealer_slug=dealer_slug, pk=invoice.pk)
@login_required
@permission_required("django_ledger.add_invoicemodel", raise_exception=True)
def invoice_create(request, pk):
def invoice_create(request,dealer_slug, pk):
"""
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
@ -4632,8 +4636,8 @@ def invoice_create(request, pk):
creation or renders the invoice creation form template otherwise.
:rtype: HttpResponse
"""
dealer = get_object_or_404(models.Dealer,slug=dealer_slug)
estimate = get_object_or_404(EstimateModel, pk=pk)
dealer = get_user_type(request)
entity = dealer.entity
if request.method == "POST":
@ -4674,7 +4678,7 @@ def invoice_create(request, pk):
estimate.save()
invoice.save()
messages.success(request, "Invoice created successfully")
return redirect("invoice_detail", pk=invoice.pk)
return redirect("invoice_detail", dealer_slug=dealer.slug,pk=invoice.pk)
else:
print(form.errors)
form = forms.InvoiceModelCreateForm(
@ -4722,7 +4726,7 @@ class InvoicePreviewView(LoginRequiredMixin, PermissionRequiredMixin, DetailView
permission_required = ["django_ledger.view_invoicemodel"]
def get_context_data(self, **kwargs):
dealer = get_user_type(self.request)
dealer = get_object_or_404(models.Dealer,slug=self.kwargs["dealer_slug"])
invoice = kwargs.get("object")
if invoice.get_itemtxs_data():
calculator = CarFinanceCalculator(invoice)
@ -4737,7 +4741,7 @@ class InvoicePreviewView(LoginRequiredMixin, PermissionRequiredMixin, DetailView
@login_required
@permission_required("django_ledger.add_journalentrymodel", raise_exception=True)
def PaymentCreateView(request, pk):
def PaymentCreateView(request,dealer_slug, pk):
"""
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
@ -4760,10 +4764,10 @@ def PaymentCreateView(request, pk):
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.
"""
dealer = get_object_or_404(models.Dealer,slug=dealer_slug)
invoice = InvoiceModel.objects.filter(pk=pk).first()
bill = BillModel.objects.filter(pk=pk).first()
model = invoice if invoice else bill
dealer = get_user_type(request)
entity = dealer.entity
form = forms.PaymentForm()
if request.method == "POST":
@ -4780,10 +4784,10 @@ def PaymentCreateView(request, pk):
model.mark_as_approved(user_model=entity.admin)
if model.amount_paid == model.amount_due:
messages.error(request, _("fully paid"))
return redirect(redirect_url, pk=model.pk)
return redirect(redirect_url, dealer_slug=dealer.slug,pk=model.pk)
if model.amount_paid + amount > model.amount_due:
messages.error(request, _("Amount exceeds due amount"))
return redirect(redirect_url, pk=model.pk)
return redirect(redirect_url, dealer_slug=dealer.slug, pk=model.pk)
try:
if invoice:
@ -4791,7 +4795,7 @@ def PaymentCreateView(request, pk):
elif bill:
set_bill_payment(dealer, entity, bill, amount, payment_method)
messages.success(request, _("Payment created successfully"))
return redirect(redirect_url, pk=model.pk)
return redirect(redirect_url, dealer_slug=dealer.slug, pk=model.pk)
except Exception as e:
messages.error(request, f"Error creating payment: {str(e)}")
else:
@ -4812,7 +4816,7 @@ def PaymentCreateView(request, pk):
@login_required
@permission_required("django_ledger.view_journalentrymodel", raise_exception=True)
def PaymentListView(request):
def PaymentListView(request,dealer_slug):
"""
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
@ -4828,7 +4832,7 @@ def PaymentListView(request):
:return: The rendered HTML response displaying the list of payments.
:rtype: HttpResponse
"""
dealer = get_user_type(request)
dealer = get_object_or_404(models.Dealer,slug=dealer_slug)
entity = dealer.entity
journals = JournalEntryModel.objects.filter(ledger__entity=entity).all()
@ -4842,7 +4846,7 @@ def PaymentListView(request):
@login_required
@permission_required("django_ledger.view_journalentrymodel", raise_exception=True)
def PaymentDetailView(request, pk):
def PaymentDetailView(request,dealer_slug, pk):
"""
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
@ -4856,6 +4860,7 @@ def PaymentDetailView(request, pk):
entry and its associated transactions.
:rtype: HttpResponse
"""
dealer = get_object_or_404(models.Dealer,slug=dealer_slug)
journal = JournalEntryModel.objects.filter(pk=pk).first()
transactions = (
TransactionModel.objects.filter(journal_entry=journal)
@ -4871,7 +4876,7 @@ def PaymentDetailView(request, pk):
@login_required
@permission_required("django_ledger.change_journalentrymodel", raise_exception=True)
def payment_mark_as_paid(request, 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
for payment. Also ensures that related ledger journal entries are locked and posted
@ -4890,6 +4895,7 @@ def payment_mark_as_paid(request, pk):
:raises: In case of an exception during the process, an error message is
displayed to the user through Django's messaging framework.
"""
dealer = get_object_or_404(models.Dealer,slug=dealer_slug)
invoice = get_object_or_404(InvoiceModel, pk=pk)
if request.method == "POST":
try:
@ -4914,7 +4920,7 @@ def payment_mark_as_paid(request, pk):
)
except Exception as e:
messages.error(request, f"Error: {str(e)}")
return redirect("invoice_detail", pk=invoice.pk)
return redirect("invoice_detail",dealer_slug=dealer_slug, pk=invoice.pk)
# activity log
@ -6645,7 +6651,8 @@ class BillModelCreateView(CreateView):
for_purchase_order = False
for_estimate = False
def get(self, request, **kwargs):
def get(self, request, dealer_slug,**kwargs):
dealer = get_object_or_404(models.Dealer,slug=dealer_slug)
if not request.user.is_authenticated:
return HttpResponseForbidden()
@ -6658,7 +6665,7 @@ class BillModelCreateView(CreateView):
)
if not estimate_model.can_bind():
return HttpResponseNotFound("404 Not Found")
return super(BillModelCreateView, self).get(request, **kwargs)
return super(BillModelCreateView, self).get(request,dealer_slug, **kwargs)
def get_context_data(self, **kwargs):
context = super(BillModelCreateView, self).get_context_data(**kwargs)
@ -6687,6 +6694,7 @@ class BillModelCreateView(CreateView):
reverse(
"bill-create-po",
kwargs={
"dealer_slug": self.kwargs["dealer_slug"],
"entity_slug": self.kwargs["entity_slug"],
"po_pk": po_model.uuid,
},
@ -6704,6 +6712,7 @@ class BillModelCreateView(CreateView):
form_action = reverse(
"bill-create-estimate",
kwargs={
"dealer_slug": self.kwargs["dealer_slug"],
"entity_slug": self.kwargs["entity_slug"],
"ce_pk": estimate_model.uuid,
},
@ -6712,6 +6721,7 @@ class BillModelCreateView(CreateView):
form_action = reverse(
"bill-create",
kwargs={
"dealer_slug": self.kwargs["dealer_slug"],
"entity_slug": self.kwargs["entity_slug"],
},
)
@ -6787,28 +6797,33 @@ class BillModelCreateView(CreateView):
po_pk = self.kwargs["po_pk"]
return reverse(
"purchase_order_update",
kwargs={"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:
return reverse(
"customer-estimate-detail",
kwargs={"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
return reverse(
"bill-detail",
kwargs={"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},
)
class BillModelDetailViewView(BillModelDetailView):
template_name = "bill/bill_detail.html"
def get_context_data(self, **kwargs):
context = super(BillModelDetailViewView, self).get_context_data(**kwargs)
context["dealer"] = self.request.dealer
return context
class BillModelUpdateViewView(BillModelUpdateView):
template_name = "bill/bill_update.html"
def post(self, request, *args, **kwargs):
def post(self, request, dealer_slug,entity_slug,bill_pk, *args, **kwargs):
if self.action_update_items:
if not request.user.is_authenticated:
return HttpResponseForbidden()
@ -6865,6 +6880,7 @@ class BillModelUpdateViewView(BillModelUpdateView):
redirect_to=reverse(
"bill-update",
kwargs={
"dealer_slug": dealer_slug,
"entity_slug": entity_model.slug,
"bill_pk": bill_pk,
},
@ -6872,12 +6888,13 @@ class BillModelUpdateViewView(BillModelUpdateView):
)
context = self.get_context_data(itemtxs_formset=itemtxs_formset)
return self.render_to_response(context=context)
return super(BillModelUpdateViewView, self).post(request, **kwargs)
return super(BillModelUpdateViewView, self).post(request,dealer_slug,entity_slug,bill_pk, **kwargs)
def get_success_url(self):
return reverse(
"bill-update",
kwargs={
"dealer_slug": self.kwargs["dealer_slug"],
"entity_slug": self.kwargs["entity_slug"],
"bill_pk": self.kwargs["bill_pk"],
},
@ -7074,7 +7091,7 @@ class OrderListView(LoginRequiredMixin, PermissionRequiredMixin, ListView):
# email
@login_required
@permission_required("django_ledger.view_estimatemodel", raise_exception=True)
def send_email_view(request, pk):
def send_email_view(request,dealer_slug, pk):
"""
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.
@ -7090,15 +7107,15 @@ def send_email_view(request, pk):
:rtype: HttpResponseRedirect
:raises Http404: If the estimate with the given primary key does not exist.
"""
dealer = get_user_type(request)
dealer = get_object_or_404(models.Dealer, slug=dealer_slug)
estimate = get_object_or_404(EstimateModel, pk=pk)
if not estimate.get_itemtxs_data()[0]:
messages.error(request, _("Quotation has no items"))
return redirect("estimate_detail", pk=estimate.pk)
return redirect("estimate_detail", dealer_slug=dealer_slug,pk=estimate.pk)
link = request.build_absolute_uri(
reverse_lazy("estimate_preview", kwargs={"pk": estimate.pk})
reverse_lazy("estimate_preview", kwargs={"dealer_slug":dealer_slug,"pk": estimate.pk})
)
msg = f"""
@ -7135,7 +7152,7 @@ def send_email_view(request, pk):
estimate.mark_as_review()
messages.success(request, _("Email sent successfully"))
return redirect("estimate_detail", pk=estimate.pk)
return redirect("estimate_detail", dealer_slug=dealer.slug,pk=estimate.pk)
# errors
@ -9041,15 +9058,15 @@ def permenant_delete_account(request, content_type, slug):
#####################################################################
def PurchaseOrderCreateView(request):
dealer = get_user_type(request)
def PurchaseOrderCreateView(request, dealer_slug):
dealer = get_object_or_404(models.Dealer, slug=dealer_slug)
entity = dealer.entity
if request.method == "POST":
po = entity.create_purchase_order(po_title=request.POST.get("po_title"))
po.entity = entity
po.save()
messages.success(request, _("Purchase order created successfully"))
return redirect("purchase_order_detail", pk=po.pk)
return redirect("purchase_order_detail", dealer_slug=dealer.slug, pk=po.pk)
form = PurchaseOrderModelCreateForm(
entity_slug=entity.slug, user_model=entity.admin
@ -9057,9 +9074,9 @@ def PurchaseOrderCreateView(request):
return render(request, "purchase_orders/po_form.html", {"form": form})
def InventoryItemCreateView(request):
def InventoryItemCreateView(request,dealer_slug):
dealer = get_object_or_404(models.Dealer, slug=dealer_slug)
for_po = request.GET.get("for_po")
dealer = get_user_type(request)
entity = dealer.entity
coa = entity.get_default_coa()
@ -9098,7 +9115,7 @@ def InventoryItemCreateView(request):
.first()
):
messages.error(request, _("Inventory item already exists"))
return redirect(f"{reverse('inventory_item_create')}?for_po={for_po}")
return redirect(f"{reverse('inventory_item_create')}?for_po={for_po}", dealer_slug=dealer.slug)
uom = entity.get_uom_all().get(name="Unit")
entity.create_item_inventory(
name=inventory_name,
@ -9108,9 +9125,10 @@ def InventoryItemCreateView(request):
coa_model=coa,
)
messages.success(request, _("Inventory item created successfully"))
return redirect("purchase_order_list")
return redirect("purchase_order_list", dealer_slug=dealer.slug)
if for_po:
form = forms.CSVUploadForm()
form.fields["vendor"].queryset = dealer.vendors.filter(active=True)
context = {
"make_data": models.CarMake.objects.all(),
"inventory_accounts": inventory_accounts,
@ -9129,8 +9147,8 @@ def InventoryItemCreateView(request):
)
def inventory_items_filter(request):
dealer = get_user_type(request)
def inventory_items_filter(request,dealer_slug):
dealer = get_object_or_404(models.Dealer, slug=dealer_slug)
make = request.GET.get("make")
model = request.GET.get("model")
serie = request.GET.get("serie")
@ -9175,38 +9193,6 @@ class PurchaseOrderDetailView(PurchaseOrderModelDetailViewBase):
return context
# def PurchaseOrderDetailView(request, pk):
# po = get_object_or_404(PurchaseOrderModel, pk=pk)
# dealer = get_user_type(request)
# make = request.GET.get('make')
# model = request.GET.get('model')
# serie = request.GET.get('serie')
# make_data = models.CarMake.objects.all()
# model_data = models.CarModel.objects.none()
# serie_data = models.CarSerie.objects.none()
# trim_data = models.CarTrim.objects.none()
# if make:
# make = models.CarMake.objects.get(pk=make)
# model_data = make.carmodel_set.all()
# elif model:
# model = models.CarModel.objects.get(pk=model)
# serie_data = model.carserie_set.all()
# elif serie:
# serie = models.CarSerie.objects.get(pk=serie)
# trim_data = serie.cartrim_set.all()
# context = {
# 'make_data': make_data,
# 'model_data': model_data,
# 'serie_data': serie_data,
# 'trim_data': trim_data,
# 'po': po,
# 'inventory_accounts': dealer.entity.get_coa_accounts().filter(role="asset_ca_inv"),
# 'inventory_items': dealer.entity.get_items_inventory(),
# }
# return render(request, "purchase_orders/po_detail.html", context)
class PurchaseOrderListView(LoginRequiredMixin, PermissionRequiredMixin, ListView):
model = PurchaseOrderModel
context_object_name = "purchase_orders"
@ -9231,15 +9217,10 @@ class PurchaseOrderUpdateView(PurchaseOrderModelUpdateViewBase):
context_object_name = "po_model"
def get_context_data(self, itemtxs_formset=None, **kwargs):
dealer = get_user_type(self.request)
dealer = self.request.dealer
context = super().get_context_data(**kwargs)
context["entity_slug"] = dealer.entity.slug
po_model: PurchaseOrderModel = self.object
# context["make_data"] = models.CarMake.objects.all()
# context["model_data"] = models.CarModel.objects.none()
# context["serie_data"] = models.CarSerie.objects.none()
# context["trim_data"] = models.CarTrim.objects.none()
# itemtxs_qs = dealer.entity.get_items_inventory().filter(item_role="inventory")
if not itemtxs_formset:
itemtxs_qs = self.get_po_itemtxs_qs(po_model)
itemtxs_qs, itemtxs_agg = po_model.get_itemtxs_data(queryset=itemtxs_qs)
@ -9261,36 +9242,35 @@ class PurchaseOrderUpdateView(PurchaseOrderModelUpdateViewBase):
return reverse(
"purchase_order_update",
kwargs={
"dealer_slug": self.kwargs["dealer_slug"],
"entity_slug": self.kwargs["entity_slug"],
"po_pk": self.kwargs["po_pk"],
},
)
def get(self, request, entity_slug, po_pk, *args, **kwargs):
def get(self, request, dealer_slug, entity_slug, po_pk, *args, **kwargs):
if self.action_update_items:
return HttpResponseRedirect(
redirect_to=reverse(
"purchase_order_update",
kwargs={"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(
request, entity_slug, po_pk, *args, **kwargs
request, dealer_slug, entity_slug, po_pk, *args, **kwargs
)
def post(self, request, entity_slug, *args, **kwargs):
dealer = get_user_type(self.request)
def post(self, request, dealer_slug, entity_slug, *args, **kwargs):
if self.action_update_items:
if not request.user.is_authenticated:
return HttpResponseForbidden()
queryset = self.get_queryset()
po_model: PurchaseOrderModel = self.get_object(queryset=queryset)
self.object = po_model
po_itemtxs_formset_class = get_po_itemtxs_formset_class(po_model)
itemtxs_formset = po_itemtxs_formset_class(
request.POST,
user_model=dealer.entity.admin,
user_model=request.dealer.entity.admin,
po_model=po_model,
entity_slug=entity_slug,
)
@ -9309,6 +9289,7 @@ class PurchaseOrderUpdateView(PurchaseOrderModelUpdateViewBase):
redirect_url = reverse(
"bill-create-po",
kwargs={
"dealer_slug": self.kwargs["dealer_slug"],
"entity_slug": self.kwargs["entity_slug"],
"po_pk": po_model.uuid,
},
@ -9337,7 +9318,7 @@ class PurchaseOrderUpdateView(PurchaseOrderModelUpdateViewBase):
context=self.get_context_data(itemtxs_formset=itemtxs_formset)
)
return super(PurchaseOrderUpdateView, self).post(
request, entity_slug, *args, **kwargs
request,dealer_slug, entity_slug, *args, **kwargs
)
def get_form(self, form_class=None):
@ -9368,19 +9349,19 @@ class PurchaseOrderUpdateView(PurchaseOrderModelUpdateViewBase):
class BasePurchaseOrderActionActionView(BasePurchaseOrderActionActionViewBase):
def get_redirect_url(self, entity_slug, po_pk, *args, **kwargs):
def get_redirect_url(self,dealer_slug, entity_slug, po_pk, *args, **kwargs):
return reverse(
"purchase_order_update", kwargs={"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, *args, **kwargs):
def get(self, request,dealer_slug, entity_slug, po_pk, *args, **kwargs):
# kwargs["user_model"] = self.request.user
dealer = get_user_type(request)
dealer = get_object_or_404(models.Dealer, slug=dealer_slug)
kwargs["user_model"] = dealer.entity.admin
if not self.action_name:
raise ImproperlyConfigured("View attribute action_name is required.")
response = super(BasePurchaseOrderActionActionView, self).get(
request, *args, **kwargs
request,dealer_slug,entity_slug,po_pk, *args, **kwargs
)
po_model: PurchaseOrderModel = self.get_object()
@ -9393,11 +9374,6 @@ class BasePurchaseOrderActionActionView(BasePurchaseOrderActionActionViewBase):
)
except ValidationError as e:
print(e)
# messages.add_message(
# request,
# message=e.message,
# level=messages.ERROR,
# )
return response
@ -9405,7 +9381,12 @@ class PurchaseOrderModelDeleteView(PurchaseOrderModelDeleteViewBase):
template_name = "purchase_orders/po_delete.html"
def get_success_url(self):
return reverse("purchase_order_list")
messages.add_message(
self.request,
message="PO deleted successfully.",
level=messages.SUCCESS,
)
return reverse("purchase_order_list", kwargs={"dealer_slug": self.kwargs["dealer_slug"]})
class PurchaseOrderMarkAsDraftView(BasePurchaseOrderActionActionView):
@ -9434,9 +9415,9 @@ class PurchaseOrderMarkAsVoidView(BasePurchaseOrderActionActionView):
##############################bil
class BaseBillActionView(BaseBillActionViewBase):
def get_redirect_url(self, entity_slug, bill_pk, *args, **kwargs):
def get_redirect_url(self,dealer_slug, entity_slug, bill_pk, *args, **kwargs):
return reverse(
"bill-update", kwargs={"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}
)
@ -9484,8 +9465,8 @@ class BillModelActionForceMigrateView(BaseBillActionView):
###############################################################
def view_items_inventory(request, entity_slug, po_pk):
dealer = get_user_type(request)
def view_items_inventory(request,dealer_slug, entity_slug, po_pk):
dealer = get_object_or_404(models.Dealer, slug=dealer_slug)
po = PurchaseOrderModel.objects.get(pk=po_pk)
items = po.get_itemtxs_data()[0]
items_per_page = 30
@ -9505,17 +9486,17 @@ def view_items_inventory(request, entity_slug, po_pk):
)
def upload_cars(request, pk=None):
def upload_cars(request,dealer_slug, pk=None):
item = None
dealer = get_user_type(request)
response = redirect("upload_cars")
dealer = get_object_or_404(models.Dealer, slug=dealer_slug)
response = redirect("upload_cars",dealer_slug=dealer_slug)
if pk:
item = get_object_or_404(ItemTransactionModel, pk=pk)
response = redirect("upload_cars", pk=pk)
response = redirect("upload_cars", dealer_slug=dealer_slug,pk=pk)
if item.item_model.additional_info.get("uploaded"):
messages.add_message(request, messages.ERROR, "Item already uploaded.")
return redirect(
"view_items_inventory", entity_slug=dealer.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":
@ -9560,7 +9541,7 @@ def upload_cars(request, pk=None):
if not csv_file.name.endswith(".csv"):
messages.error(request, "Please upload a CSV file")
return redirect("upload_cars")
return redirect("upload_cars",dealer_slug=dealer_slug)
try:
# Read the file content
file_content = csv_file.read().decode("utf-8")

View File

@ -42,11 +42,11 @@
<div class="col-lg-4">
<div class="card shadow-sm" >
<div class="card-body">
{% include 'bill/includes/card_bill.html' with bill=bill entity_slug=view.kwargs.entity_slug style='bill-detail' %}
{% include 'bill/includes/card_bill.html' with dealer_slug=request.dealer.slug bill=bill entity_slug=view.kwargs.entity_slug style='bill-detail' %}
<hr class="my-4">
{% include 'bill/includes/card_vendor.html' with vendor=bill.vendor %}
<div class="d-grid mt-4">
<a href="{% url 'bill_list' %}"
<a href="{% url 'bill_list' request.dealer.slug %}"
class="btn btn-phoenix-primary">
<i class="fas fa-arrow-left me-1"></i> {% trans 'Bill List' %}
</a>
@ -65,7 +65,7 @@
<div class="border rounded p-3">
<h6 class="text-uppercase text-xs text-muted mb-2">
{% trans 'Cash Account' %}:
<a href="{% url 'account_detail' bill.cash_account.uuid %}"
<a href="{% url 'account_detail' request.dealer.slug bill.cash_account.uuid %}"
class="text-decoration-none ms-1">
{{ bill.cash_account.code }}
</a>
@ -80,7 +80,7 @@
<div class="border rounded p-3">
<h6 class="text-uppercase text-xs text-muted mb-2">
{% trans 'Prepaid Account' %}:
<a href="{% url 'account_detail' bill.prepaid_account.uuid %}"
<a href="{% url 'account_detail' request.dealer.slug bill.prepaid_account.uuid %}"
class="text-decoration-none ms-1">
{{ bill.prepaid_account.code }}
</a>
@ -94,7 +94,7 @@
<div class="border rounded p-3">
<h6 class="text-uppercase text-xs text-muted mb-2">
{% trans 'Accounts Payable' %}:
<a href="{% url 'account_detail' bill.unearned_account.uuid %}"
<a href="{% url 'account_detail' request.dealer.slug bill.unearned_account.uuid %}"
class="text-decoration-none ms-1">
{{ bill.unearned_account.code }}
</a>
@ -185,7 +185,7 @@
<td class="align-items-start white-space-nowrap pe-2">
{% if bill_item.po_model_id %}
<a class="btn btn-sm btn-phoenix-primary"
href="{% url 'purchase_order_detail' bill_item.po_model_id %}">
href="{% url 'purchase_order_detail' request.dealer.slug bill_item.po_model_id %}">
{% trans 'View PO' %}
</a>
{% endif %}

View File

@ -17,9 +17,9 @@
<div class="col-12">
<div class="card mb-4">
<div class="card-body">
{% include 'bill/includes/card_bill.html' with bill=bill_model style='bill-detail' entity_slug=view.kwargs.entity_slug %}
{% include 'bill/includes/card_bill.html' with dealer_slug=request.dealer.slug bill=bill_model style='bill-detail' entity_slug=view.kwargs.entity_slug %}
<form action="{% url 'bill-update' entity_slug=view.kwargs.entity_slug bill_pk=bill_model.uuid %}" method="post">
<form action="{% url 'bill-update' dealer_slug=request.dealer.slug entity_slug=view.kwargs.entity_slug bill_pk=bill_model.uuid %}" method="post">
{% csrf_token %}
<div class="mb-3">
@ -30,12 +30,12 @@
<i class="fas fa-save me-2"></i>{% trans 'Save Bill' %}
</button>
<a href="{% url 'bill-detail' entity_slug=view.kwargs.entity_slug bill_pk=bill_model.uuid %}"
<a href="{% url 'bill-detail' dealer_slug=request.dealer.slug entity_slug=view.kwargs.entity_slug bill_pk=bill_model.uuid %}"
class="btn btn-phoenix-secondary w-100 mb-2">
<i class="fas fa-arrow-left me-2"></i>{% trans 'Back to Bill Detail' %}
</a>
<a href="{% url 'bill_list' %}"
<a href="{% url 'bill_list' request.dealer.slug %}"
class="btn btn-phoenix-info w-100 mb-2">
<i class="fas fa-list me-2"></i>{% trans 'Bill List' %}
</a>

View File

@ -201,48 +201,48 @@
<div class="card-footer p-0">
<div class="d-flex flex-wrap gap-2 mt-2">
<!-- Update Button -->
<a href="{% url 'bill-update' entity_slug=entity_slug bill_pk=bill.uuid %}" class="btn btn-phoenix-primary">
<a href="{% url 'bill-update' dealer_slug=dealer_slug entity_slug=entity_slug bill_pk=bill.uuid %}" class="btn btn-phoenix-primary">
<i class="fas fa-edit me-2"></i>{% trans 'Update' %}
</a>
<!-- Mark as Draft -->
{% if bill.can_draft %}
<button class="btn btn-phoenix-success"
onclick="showPOModal('Mark as Draft', '{% url 'bill-action-mark-as-draft' entity_slug=entity_slug bill_pk=bill.pk %}', 'Mark as Draft')">
onclick="showPOModal('Mark as Draft', '{% url 'bill-action-mark-as-draft' dealer_slug=request.dealer.slug entity_slug=entity_slug bill_pk=bill.pk %}', 'Mark as Draft')">
<i class="fas fa-check-circle me-2"></i>{% trans 'Mark as Draft' %}
</button>
{% endif %}
<!-- Mark as Review -->
{% if bill.can_review %}
<button class="btn btn-phoenix-warning"
onclick="showPOModal('Mark as Review', '{% url 'bill-action-mark-as-review' entity_slug=entity_slug bill_pk=bill.pk %}', 'Mark as Review')">
onclick="showPOModal('Mark as Review', '{% url 'bill-action-mark-as-review' dealer_slug=request.dealer.slug entity_slug=entity_slug bill_pk=bill.pk %}', 'Mark as Review')">
<i class="fas fa-check-circle me-2"></i>{% trans 'Mark as Review' %}
</button>
{% endif %}
<!-- Mark as Approved -->
{% if bill.can_approve %}
<button class="btn btn-phoenix-success"
onclick="showPOModal('Mark as Approved', '{% url 'bill-action-mark-as-approved' entity_slug=entity_slug bill_pk=bill.pk %}', 'Mark as Approved')">
onclick="showPOModal('Mark as Approved', '{% url 'bill-action-mark-as-approved' dealer_slug=request.dealer.slug entity_slug=entity_slug bill_pk=bill.pk %}', 'Mark as Approved')">
<i class="fas fa-check-circle me-2"></i>{% trans 'Mark as Approved' %}
</button>
{% endif %}
<!-- Mark as Paid -->
{% if bill.can_pay %}
<button class="btn btn-phoenix-success"
onclick="showPOModal('Mark as Paid', '{% url 'bill-action-mark-as-paid' entity_slug=entity_slug bill_pk=bill.pk %}', 'Mark as Paid')">
onclick="showPOModal('Mark as Paid', '{% url 'bill-action-mark-as-paid' dealer_slug=request.dealer.slug entity_slug=entity_slug bill_pk=bill.pk %}', 'Mark as Paid')">
<i class="fas fa-check-circle me-2"></i>{% trans 'Mark as Paid' %}
</button>
{% endif %}
<!-- Void Button -->
{% if bill.can_void %}
<button class="btn btn-phoenix-danger"
onclick="showPOModal('Mark as Void', '{% url 'bill-action-mark-as-void' entity_slug=entity_slug bill_pk=bill.pk %}', 'Mark as Void')">
onclick="showPOModal('Mark as Void', '{% url 'bill-action-mark-as-void' dealer_slug=request.dealer.slug entity_slug=entity_slug bill_pk=bill.pk %}', 'Mark as Void')">
<i class="fas fa-check-circle me-2"></i>{% trans 'Mark as Void' %}
</button>
{% endif %}
<!-- Cancel Button -->
{% if bill.can_cancel %}
<button class="btn btn-phoenix-danger"
onclick="showPOModal('Mark as Canceled', '{% url 'bill-action-mark-as-canceled' entity_slug=entity_slug bill_pk=bill.pk %}', 'Mark as Canceled')">
onclick="showPOModal('Mark as Canceled', '{% url 'bill-action-mark-as-canceled' dealer_slug=request.dealer.slug entity_slug=entity_slug bill_pk=bill.pk %}', 'Mark as Canceled')">
<i class="fas fa-check-circle me-2"></i>{% trans 'Mark as Canceled' %}
</button>
{% modal_action_v2 bill bill.get_mark_as_canceled_url bill.get_mark_as_canceled_message bill.get_mark_as_canceled_html_id %}

View File

@ -3,7 +3,7 @@
{% load django_ledger %}
{% load widget_tweaks %}
<form action="{% url 'bill-update-items' entity_slug=entity_slug bill_pk=bill_pk %}" method="post">
<form action="{% url 'bill-update-items' dealer_slug=dealer_slug entity_slug=entity_slug bill_pk=bill_pk %}" method="post">
<div class="container-fluid py-4">
<!-- Page Header -->
<div class="row mb-4">
@ -72,7 +72,7 @@
{% currency_symbol %}{{ f.instance.po_total_amount | currency_format }}
</span>
<a class="btn btn-sm btn-phoenix-info mt-1"
href="{% url 'purchase_order_detail' f.instance.po_model_id %}">
href="{% url 'purchase_order_detail' dealer_slug f.instance.po_model_id %}">
{% trans 'View PO' %}
</a>
</div>

View File

@ -27,7 +27,7 @@
</div>
<div class="col-6 col-md-4 col-xxl-2 text-center border-translucent border-start-xxl border-end-xxl-0 border-bottom-xxl-0 border-end border-bottom pb-4 pb-xxl-0 ">
<span class="uil fs-5 lh-1 uil-bill text-info"></span>
<a href="{% url 'invoice_list' %}"><h4 class="fs-6 pt-3">{{ invoices }}</h4></a>
<a href="{% url 'invoice_list' request.dealer.slug %}"><h4 class="fs-6 pt-3">{{ invoices }}</h4></a>
<p class="fs-9 mb-0">{{ _("Invoices")}}</p>
</div>
<div class="col-6 col-md-4 col-xxl-2 text-center border-translucent border-start-xxl border-end-xxl-0 border-bottom-xxl-0 border-end border-bottom pb-4 pb-xxl-0 ">

View File

@ -43,6 +43,6 @@
<div class="error-page">
<h1 class="error-code">{% trans "400" %}</h1>
<p class="error-message">{% trans "Bad Request" %}</p>
<a href="{% url 'inventory_stats' %}" class="error-button">{% trans "Go Back" %}</a>
<a href="{% url 'inventory_stats' request.dealer.slug %}" class="error-button">{% trans "Go Back" %}</a>
</div>
{% endblock content %}

View File

@ -22,44 +22,44 @@
<li class="collapsed-nav-item-title d-none">{% trans "Inventory"|capfirst %}</li>
{% if perms.inventory.add_car %}
<li class="nav-item">
<a id="btn-add-car" class="nav-link btn-add-car" href="{% url 'car_add' %}">
<a id="btn-add-car" class="nav-link btn-add-car" href="{% url 'car_add' request.dealer.slug %}">
<div class="d-flex align-items-center">
<span class="nav-link-icon"><span class="fas fa-plus-circle"></span></span><span class="nav-link-text">{% trans "add car"|capfirst %}</span>
</div>
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{% url 'inventory_item_create' %}">
<a class="nav-link" href="{% url 'inventory_item_create' request.dealer.slug %}">
<div class="d-flex align-items-center">
<span class="nav-link-icon"><span class="fas fa-plus-circle"></span></span><span class="nav-link-text">{% trans "add invenotry item"|capfirst %}</span>
</div>
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{% url 'purchase_order_list' %}">
<a class="nav-link" href="{% url 'purchase_order_list' request.dealer.slug %}">
<div class="d-flex align-items-center">
<span class="nav-link-icon"><span class="fas fa-warehouse"></span></span><span class="nav-link-text">{% trans "purchase Orders"|capfirst %}</span>
</div>
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{% url 'upload_cars' %}">
<a class="nav-link" href="{% url 'upload_cars' request.dealer.slug %}">
<div class="d-flex align-items-center">
<span class="nav-link-icon"><span class="fas fa-file-invoice"></span></span><span class="nav-link-text">{% trans "Upload Cars"|capfirst %}</span>
<span class="nav-link-icon"><span class="fas fa-file-import"></span></span><span class="nav-link-text">{% trans "Bulk Upload"|capfirst %}</span>
</div>
</a>
</li>
{% endif %}
<li class="nav-item">
<a class="nav-link" href="{% url 'inventory_stats' %}">
<a class="nav-link" href="{% url 'inventory_stats' request.dealer.slug %}">
<div class="d-flex align-items-center">
<span class="nav-link-icon"><span class="fas fa-car-side"></span></span><span class="nav-link-text">{% trans 'Cars'|capfirst %}</span>
</div>
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{% url 'car_list' %}">
<a class="nav-link" href="{% url 'car_list' request.dealer.slug %}">
<div class="d-flex align-items-center">
<span class="nav-link-icon"><span class="fas fa-car-side"></span></span><span class="nav-link-text">{% trans 'Stock'|capfirst %}</span>
</div>
@ -82,7 +82,7 @@
<li class="collapsed-nav-item-title d-none">{% trans 'sales'|capfirst %}</li>
{% if perms.django_ledger.add_estimatemodel %}
<li class="nav-item">
<a class="nav-link" href="{% url 'estimate_create' %}">
<a class="nav-link" href="{% url 'estimate_create' request.dealer.slug %}">
<div class="d-flex align-items-center">
<span class="nav-link-icon"><span class="fas fa-list-ul"></span></span><span class="nav-link-text">{% trans "create quotation"|capfirst %}</span>
</div>
@ -91,7 +91,7 @@
{% endif %}
{% if perms.django_ledger.view_estimatemodel %}
<li class="nav-item">
<a class="nav-link" href="{% url 'estimate_list' %}">
<a class="nav-link" href="{% url 'estimate_list' request.dealer.slug %}">
<div class="d-flex align-items-center">
<span class="nav-link-icon"><span class="fas fa-clipboard-list"></span></span><span class="nav-link-text">{% trans "quotations"|capfirst %}</span>
</div>
@ -101,7 +101,7 @@
{% if perms.django_ledger.view_invoicemodel %}
<li class="nav-item">
<a class="nav-link" href="{% url 'invoice_list' %}">
<a class="nav-link" href="{% url 'invoice_list' request.dealer.slug %}">
<div class="d-flex align-items-center">
<span class="nav-link-icon"><span class="fas fa-file-invoice"></span></span><span class="nav-link-text">{% trans "invoices"|capfirst %}</span>
</div>
@ -110,7 +110,7 @@
{% endif %}
{% if perms.django_ledger.view_journalentrymodel %}
<li class="nav-item">
<a class="nav-link" href="{% url 'payment_list' %}">
<a class="nav-link" href="{% url 'payment_list' request.dealer.slug %}">
<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 "payments"|capfirst %}</span>
</div>
@ -212,7 +212,7 @@
<li class="collapsed-nav-item-title d-none">{% trans 'Financials' %}</li>
{% if perms.django_ledger.view_accountmodel %}
<li class="nav-item">
<a class="nav-link" href="{% url 'account_list' %}">
<a class="nav-link" href="{% url 'account_list' request.dealer.slug %}">
<div class="d-flex align-items-center">
<span class="nav-link-icon"><span class="fas fa-book-open"></span></span><span class="nav-link-text">{% trans 'Chart of Accounts'|capfirst %}</span>
</div>
@ -221,7 +221,7 @@
{% endif %}
{% if perms.django_ledger.view_bankaccountmodel %}
<li class="nav-item">
<a class="nav-link" href="{% url 'bank_account_list' %}">
<a class="nav-link" href="{% url 'bank_account_list' request.dealer.slug %}">
<div class="d-flex align-items-center">
<span class="nav-link-icon"><span data-feather="credit-card"></span></span><span class="nav-link-text">{% trans 'Bank Accounts'|capfirst %}</span>
</div>
@ -264,7 +264,7 @@
{% endif %}
{% if perms.django_ledger.view_billmodel %}
<li class="nav-item">
<a class="nav-link" href="{% url 'bill_list' %}">
<a class="nav-link" href="{% url 'bill_list' request.dealer.slug %}">
<div class="d-flex align-items-center">
<span class="nav-link-icon"><span class="fa-solid fa-money-bills"></span></span><span class="nav-link-text">{% trans 'bills'|capfirst %}</span>
</div>
@ -362,7 +362,7 @@
aria-label="Toggle Navigation">
<span class="navbar-toggle-icon"><span class="toggle-line"></span></span>
</button>
<a class="navbar-brand me-1 me-sm-3" href="{% url 'home' %}">
<a class="navbar-brand me-1 me-sm-3" href="{% url 'home' request.dealer.slug %}">
<div class="d-flex align-items-center">
<img class="logo-img d-dark-none" src="{% static 'images/logos/logo-d.png' %}" alt="haikal" width="27" />
<img class="logo-img d-light-none" src="{% static 'images/logos/logo.png' %}" alt="haikal" width="27" />

View File

@ -15,5 +15,4 @@
</div>
</div>
{% endif %}
{% endblock %}

View File

@ -2,7 +2,7 @@
{% load crispy_forms_filters %}
<form method="post"
id="customCardForm"
action="{% url 'add_custom_card' car.slug %}">
action="{% url 'add_custom_card' request.dealer.slug car.slug %}">
{% csrf_token %}
{{ form|crispy }}
<div class="d-flex gap-1">

View File

@ -163,10 +163,14 @@
<tr>
<th>{% trans "Custom Card" %}</th>
<td>
<button type="button"
<button type="button"
class="btn btn-sm btn-phoenix-success"
data-bs-toggle="modal"
data-bs-target="#customCardModal">{% trans 'Add' %}</button>
data-bs-target="#mainModal"
hx-get="{% url 'add_custom_card' request.dealer.slug car.slug %}"
hx-target=".main-modal-body"
hx-swap="innerHTML"
>{% trans 'Add' %}</button>
</td>
</tr>
{% endif %}
@ -188,7 +192,12 @@
<button type="button"
class="btn btn-sm btn-phoenix-success"
data-bs-toggle="modal"
data-bs-target="#registrationModal">{% trans 'Add' %}</button>
data-bs-target="#mainModal"
hx-get="{% url 'add_registration' request.dealer.slug car.slug %}"
hx-target=".main-modal-body"
hx-swap="innerHTML"
>{% trans 'Add' %}</button>
</td>
</tr>
{% endif %}
@ -219,7 +228,7 @@
<div>
{% if not car.get_transfer %}
{% if perms.inventory.change_car %}
<a href="{% url 'car_update' car.slug %}"
<a href="{% url 'car_update' request.dealer.slug car.slug %}"
class="btn btn-phoenix-warning btn-sm mt-1">{% trans "Edit" %}</a>
<a href="{% url 'transfer' car.slug %}"
class="btn btn-phoenix-danger btn-sm">
@ -277,7 +286,7 @@
<tr>
<td colspan="2">
{% if not car.get_transfer %}
<a href="{% url 'car_finance_update' car.finances.pk %}"
<a href="{% url 'car_finance_update' request.dealer.slug car.finances.pk %}"
class="btn btn-phoenix-warning btn-sm mb-3">{% trans "Edit" %}</a>
{% else %}
<span class="badge bg-danger">{% trans "Cannot Edit, Car in Transfer." %}</span>
@ -285,7 +294,7 @@
{% else %}
<p>{% trans "No finance details available." %}</p>
{% if perms.inventory.add_carfinance %}
<a href="{% url 'car_finance_create' car.slug %}"
<a href="{% url 'car_finance_create' request.dealer.slug car.slug %}"
class="btn btn-phoenix-success btn-sm mb-3">{% trans "Add" %}</a>
{% endif %}
</td>
@ -327,7 +336,7 @@
<tr>
<td colspan="2">
{% if not car.get_transfer %}
<a href="{% url 'car_colors_update' car.slug %}"
<a href="{% url 'car_colors_update' request.dealer.slug car.slug %}"
class="btn btn-phoenix-warning btn-sm mb-3">{% trans "Edit" %}</a>
{% else %}
<span class="badge bg-danger">{% trans "Cannot Edit, Car in Transfer." %}</span>
@ -340,7 +349,7 @@
<td colspan="2">
<p>{% trans "No color details available." %}</p>
{% if perms.inventory.add_carcolors %}
<a class="btn btn-phoenix-success btn-sm mb-3" href="{% url 'add_color' car.slug %}">{{ _("Add Color") }}</a>
<a class="btn btn-phoenix-success btn-sm mb-3" href="{% url 'add_color' request.dealer.slug car.slug %}">{{ _("Add Color") }}</a>
{% endif %}
</td>
</tr>
@ -372,7 +381,7 @@
<td>{{ reservation.reserved_until }}</td>
<td>
{% if reservation.is_active %}
<form method="post" action="{% url 'reservations' reservation.id %}">
<form method="post" action="{% url 'reservations' request.dealer.slug reservation.id %}">
{% csrf_token %}
<div class="btn-group">
<button type="submit"
@ -495,22 +504,22 @@
</div>
</div>
</div>
<!-- Registration Modal -->
<!-- Main Modal -->
<div class="modal fade"
id="registrationModal"
id="mainModal"
tabindex="-1"
aria-labelledby="registrationModalLabel"
aria-labelledby="mainModalLabel"
aria-hidden="true">
<div class="modal-dialog modal-sm">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="registrationModalLabel">{% trans 'Registration' %}</h5>
<h5 class="modal-title" id="mainModalLabel">{% trans 'Registration' %}</h5>
<button type="button"
class="btn-close"
data-bs-dismiss="modal"
aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="main-modal-body" style="padding: 20px;">
<!-- Content will be loaded here via AJAX -->
</div>
</div>
@ -532,7 +541,7 @@
aria-label="Close"></button>
</div>
<div class="modal-body">{% trans 'Are you sure you want to reserve this car?' %}</div>
<form method="POST" action="{% url 'reserve_car' car.slug %}" class="form ">
<form method="POST" action="{% url 'reserve_car' request.dealer.slug car.slug %}" class="form">
{% csrf_token %}
<div class="p-1">
<div class="d-flex gap-1">
@ -577,52 +586,11 @@
<script>
document.addEventListener("DOMContentLoaded", function () {
const csrftoken = getCookie("csrftoken");
const ajaxUrl = "{% url 'ajax_handler' %}";
const ajaxUrl = "{% url 'ajax_handler' request.dealer.slug %}";
const customCardModal = document.getElementById("customCardModal");
const modalBody = customCardModal.querySelector(".modal-body");
// When the modal is triggered, load the form
customCardModal.addEventListener("show.bs.modal", function () {
const url = "{% url 'add_custom_card' car.slug %}";
fetch(url)
.then((response) => response.text())
.then((html) => {
modalBody.innerHTML = html;
})
.catch((error) => {
modalBody.innerHTML = '<p class="text-danger">Error loading form. Please try again later.</p>';
console.error("Error loading form:", error);
});
});
customCardModal.addEventListener("hidden.bs.modal", function () {
modalBody.innerHTML = "";
});
const registrationModal = document.getElementById("registrationModal");
const modalBody_r = registrationModal.querySelector(".modal-body");
// When the modal is triggered, load the form
registrationModal.addEventListener("show.bs.modal", function () {
const url = "{% url 'add_registration' car.slug %}";
fetch(url)
.then((response) => response.text())
.then((html) => {
modalBody_r.innerHTML = html;
})
.catch((error) => {
modalBody_r.innerHTML = '<p class="text-danger">{{_("Error loading form. Please try again later")}}.</p>';
console.error("Error loading form:", error);
});
});
registrationModal.addEventListener("hidden.bs.modal", function () {
modalBody_r.innerHTML = "";
});
const showSpecificationButton = document.getElementById("specification-btn");
const specificationsContent = document.getElementById("specificationsContent");
@ -689,7 +657,7 @@
document.querySelectorAll(".reserve-btn").forEach((button) => {
button.addEventListener("click", async function () {
try {
const response = await fetch(`{% url 'reserve_car' car.slug %}`, {
const response = await fetch(`{% url 'reserve_car' request.dealer.slug car.slug %}`, {
method: "POST",
headers: {
"X-CSRFToken": csrfToken,

View File

@ -29,13 +29,6 @@
{{ form|crispy }}
</div>
</div>
{% comment %} <div class="row g-1">
<div class="btn-group">
<button type="submit" class="btn btn-sm btn-success me-1"><i class="fa-solid fa-floppy-disk"></i>{% trans "Save" %}</button>
<a href="{{request.META.HTTP_REFERER}}" class="btn btn-sm btn-danger"><i class="fa-solid fa-ban"></i>{% trans "Cancel" %}</a>
</div>
</div> {% endcomment %}
<div class="d-flex justify-content-center">
<button class="btn btn-sm btn-phoenix-success me-2" type="submit">
<i class="fa-solid fa-floppy-disk me-1"></i>

View File

@ -346,7 +346,7 @@
const optionsContent = document.getElementById("optionsContent")
const generationContainer = document.getElementById("generation-div")
const ajaxUrl = "{% url 'ajax_handler' %}";
const ajaxUrl = "{% url 'ajax_handler' request.dealer.slug %}";
const closeButton = document.querySelector(".btn-close");
const scanVinBtn = document.getElementById("scan-vin-btn");

View File

@ -330,7 +330,7 @@
const optionsContent = document.getElementById("optionsContent")
const generationContainer = document.getElementById("generation-div")
const ajaxUrl = "{% url 'ajax_handler' %}";
const ajaxUrl = "{% url 'ajax_handler' request.dealer.slug %}";
const closeButton = document.querySelector(".btn-close");
const scanVinBtn = document.getElementById("scan-vin-btn");

View File

@ -93,7 +93,7 @@
{% endif %}
</td>
<td class="align-middle white-space-nowrap text-start">
<a class="fs-9 fw-bold" href="{% url 'car_detail' car.slug %}">{{ car.vin }}</a>
<a class="fs-9 fw-bold" href="{% url 'car_detail' request.dealer.slug car.slug %}">{{ car.vin }}</a>
</td>
<td class="align-middle white-space-nowrap text-center fw-bold">{{ car.year }}</td>
{% if car.colors %}
@ -147,14 +147,14 @@
</td>
<td class="align-middle white-space-nowrap text-end pe-0 ps-4">
<a class="btn btn-sm btn-phoenix-success"
href="{% url 'car_detail' car.slug %}">{% trans "view"|capfirst %}</a>
href="{% url 'car_detail' request.dealer.slug car.slug %}">{% trans "view"|capfirst %}</a>
</td>
</tr>
{% empty %}
<tr>
<td colspan="7" class="d-flex flex-column align-items-center">
<p class="text-muted">{% trans "No cars available." %}</p>
<a href="{% url 'add_car' %}" class="btn btn-phoenix-primary">{% trans "Add a Car" %}</a>
<a href="{% url 'add_car' request.dealer.slug %}" class="btn btn-phoenix-primary">{% trans "Add a Car" %}</a>
</td>
</tr>
{% endfor %}

View File

@ -44,19 +44,19 @@
hx-on::before-request="on_before_request()"
hx-on::after-request="on_after_request()">
<li class="nav-item">
<a class="nav-link px-2 py-1 active" aria-current="page" href="{% url 'car_list' %}"><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 class="nav-item">
<a class="nav-link px-2 py-1" href="{% url 'car_list' %}?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 class="nav-item">
<a class="nav-link px-2 py-1" href="{% url 'car_list' %}?status=reserved"><span>{{ _("Reserved") }}</span><span class="text-body-tertiary fw-semibold">({{ stats.reserved }})</span></a>
<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 class="nav-item">
<a class="nav-link px-2 py-1" href="{% url 'car_list' %}?status=transfer"><span>{{ _("Transfer") }}</span><span class="text-body-tertiary fw-semibold">({{ stats.transfer }})</span></a>
<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 class="nav-item">
<a class="nav-link px-2 py-1" href="{% url 'car_list' %}?status=sold"><span>{{ _("Sold") }}</span><span class="text-body-tertiary fw-semibold">({{ stats.sold }})</span></a>
<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 class="nav-item">
<button hx-on:click="toggle_filter()" class="btn btn-sm btn-phoenix-primary px-2 py-1">
@ -88,7 +88,7 @@
type="search"
placeholder="Search"
aria-label="Search"
hx-get="{% url 'car_list' %}"
hx-get="{% url 'car_list' request.dealer.slug %}"
hx-trigger="keyup changed delay:500ms"
hx-target=".table-responsive"
hx-select=".table-responsive"
@ -106,7 +106,7 @@
<div class="row">
<div class="d-flex align-items-center d-none filter">
<select
hx-get="{% url 'car_list' %}"
hx-get="{% url 'car_list' request.dealer.slug %}"
name="make"
hx-target=".model-select"
hx-select=".model-select"
@ -122,7 +122,7 @@
{% endfor %}
</select>
<select
hx-get="{% url 'car_list' %}"
hx-get="{% url 'car_list' request.dealer.slug %}"
hx-include=".make"
name="model"
hx-target=".year"
@ -153,7 +153,7 @@
</select>
<button
id="search"
hx-get="{% url 'car_list' %}"
hx-get="{% url 'car_list' request.dealer.slug %}"
hx-include=".make,.model,.year,.car_status"
hx-indicator=".htmx-indicator"
hx-target=".table-responsive"
@ -177,121 +177,119 @@
hx-swap="innerHTML show:window:top"
hx-indicator=".htmx-indicator"
hx-on::before-request="on_before_request()"
hx-on::after-request="on_after_request()">
</div>
<table class="table fs-9 mb-0 border-top border-translucent">
<thead>
<tr>
<th class="sort white-space-nowrap align-middle" scope="col">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="select-all" />
</div>
</th>
<th class="sort white-space-nowrap align-middle" scope="col">{{ _("VIN") }}</th>
<th class="sort white-space-nowrap align-middle" scope="col" data-sort="make">{{ _("Make") }}</th>
<th class="sort white-space-nowrap align-middle" scope="col" data-sort="model">{{ _("Model") }}</th>
<th class="sort white-space-nowrap align-middle" scope="col" data-sort="year">{{ _("Year") }}</th>
<th class="sort white-space-nowrap align-middle" scope="col">{{ _("Trim") }}</th>
<th class="sort white-space-nowrap align-middle" scope="col">{{ _("Color") }}</th>
<th class="sort white-space-nowrap align-middle" scope="col">{{ _("Date Received") }}</th>
<th class="sort white-space-nowrap align-middle" scope="col" data-sort="car_status">{{ _("Status") }}</th>
<th class="sort white-space-nowrap align-middle" scope="col">{{ _("Inventory Ready") }}</th>
<th class="sort white-space-nowrap align-middle" scope="col"></th>
</tr>
</thead>
<tbody class="list" id="project-list-table-body">
{% for car in cars %}
<tr class="position-static">
<td class="align-middle time white-space-nowrap">
<div class="form-check">
<input class="form-check-input car-checkbox"
type="checkbox"
name="car"
value="{{ car.pk }}"
id="car-{{car.pk}}" />
</div>
</td>
<td class="align-middle white-space-nowrap assignees">
<a class="fw-bold" href="{% url 'car_detail' car.slug %}">{{ car.vin }}</a>
</td>
<td class="align-middle white-space-nowrap make">
<p class="mb-0 fs-9 text-body">{{ car.id_car_make.get_local_name|default:car.id_car_make.name }}</p>
</td>
<td class="align-middle white-space-nowrap model">
<p class="mb-0 fs-9 text-body">{{ car.id_car_model.get_local_name|default:car.id_car_model.name }}</p>
</td>
<td class="align-middle white-space-nowrap year">
<p class="fw-bo text-body fs-9 mb-0">{{ car.year }}</p>
</td>
<td class="align-middle white-space-nowrap">
<p class="text-body-secondary fs-10 mb-0">{{ car.id_car_trim }}</p>
</td>
<td class="align-middle white-space-nowrap">
<div class="d-flex flex-row align-items-center">
<div class="d-flex flex-column align-items-center">
<span class="color-div"
style="background: linear-gradient(90deg, rgba({{ car.colors.exterior.rgb }},1) 10%, rgba({{ car.colors.exterior.rgb }},0.10) 100%)"
title="{{ car.colors.exterior.get_local_name }}"></span><span>{{ car.colors.exterior.get_local_name }}</span>
</div>
<div class="d-flex flex-column align-items-center">
<span class="color-div"
style="background: linear-gradient(90deg, rgba({{ car.colors.interior.rgb }},1) 10%, rgba({{ car.colors.interior.rgb }},0.10) 100%)"
title="{{ car.colors.interior.get_local_name }}"></span><span>{{ car.colors.interior.get_local_name }}</span>
</div>
</div>
</td>
<td class="align-middle white-space-nowrap">
<p class="fw-bold text-body mb-0">{{ car.receiving_date|naturalday|capfirst }}</p>
</td>
<td class="align-middle white-space-nowrap statuses">
{% if car.status == "available" %}
<span class="badge badge-phoenix fs-11 badge-phoenix-success">{{ _("Available") }}</span>
{% elif car.status == "reserved" %}
<span class="badge badge-phoenix fs-11 badge-phoenix-danger">{{ _("Reserved") }}</span>
{% elif car.status == "sold" %}
<span class="badge badge-phoenix fs-11 badge-phoenix-info">{{ _("Sold") }}</span>
{% elif car.status == "transfer" %}
<span class="badge badge-phoenix fs-11 badge-phoenix-warning">{{ _("Transfer") }}</span>
{% endif %}
</td>
<td class="align-middle white-space-nowrap">
{% if not car.ready %}
<span class="text-danger"> {{ _("NO") }} </span>
{%else%}
<span class="text-success"> {{ _("YES") }} </span>
{%endif%}
</td>
<td class="align-middle text-end white-space-nowrap pe-0 action">
<div class="btn-reveal-trigger position-static">
<button class="btn btn-sm dropdown-toggle dropdown-caret-none transition-none btn-reveal fs-10"
type="button"
data-bs-toggle="dropdown"
data-boundary="window"
aria-haspopup="true"
aria-expanded="false"
data-bs-reference="parent">
<span class="fas fa-ellipsis-h fs-10"></span>
</button>
<div class="dropdown-menu dropdown-menu-end py-2">
<a class="dropdown-item" href="{% url 'car_detail' car.slug %}">{{ _("View") }}</a>
<a class="dropdown-item" href="#!">{{ _("Export") }}</a>
</div>
</div>
</td>
</tr>
{% empty %}
<div class="col-12">
<div class="text-center py-5">
<div class="text-body-secondary">
<i class="fas fa-car fa-4x mb-3 opacity-50"></i>
<h4 class="text-body-secondary">{{ _("No vehicles found") }}</h4>
<p class="text-body-tertiary">{{ _("Try adjusting your search criteria or filters") }}</p>
</div>
</div>
hx-on::after-request="on_after_request()"></div>
<div class="w-100 list table-responsive" >
<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>
</div>
{% for car in cars %}
<div class="card border mb-3 py-0 px-0" id="project-list-table-body">
<div class="card-body">
<div class="row align-items-center">
<div class="col-auto">
<div class="form-check">
<input class="form-check-input car-checkbox" type="checkbox" name="car" value="{{ car.pk }}" id="car-{{car.pk}}" />
</div>
{% endfor %}
</tbody>
</table>
</div>
<!-- Vehicle Image/Icon -->
<div class="col-auto">
<div class="avatar avatar-3xl">
<img class="rounded-soft shadow shadow-lg" src="{% static 'images/cars/' %}{{ car.vin }}.png" alt="{{ car.vin }}" />
</div>
</div>
<!-- Vehicle Details -->
<div class="col">
<div class="row">
<!-- Make/Model/Specs -->
<div class="col-md-4">
<h5 class="text-body-emphasis fw-bold">{{ car.year }}</h5>
<p class="text-body-emphasis fw-bold">
<a href="{% url 'car_detail' request.dealer.slug car.slug %}" class="text-decoration-none text-body-emphasis">
{{ car.id_car_make.get_local_name }}
</a>
<small>{{ car.id_car_model.get_local_name }}</small>
</p>
<small class="text-body-secondary" dir="ltr">
{{ car.id_car_trim }}
</small>
</div>
<!-- Color and Date -->
<div class="col-md-3">
<p class="text-body mb-1">
{{ car.colors.exterior.get_local_name }}
</p>
<small class="text-body-secondary">
{{ car.receiving_date|naturalday|capfirst }}
</small>
</div>
<!-- Status Badge -->
<div class="col-md-3">
{% if car.status == "available" %}
<span class="badge badge-phoenix fs-10 badge-phoenix-success text-uppercase px-3 py-2">{{ _("Available") }}</span>
{% elif car.status == "reserved" %}
<span class="badge badge-phoenix fs-10 badge-phoenix-warning text-uppercase px-3 py-2">{{ _("Reserved") }}</span>
{% elif car.status == "sold" %}
<span class="badge badge-phoenix fs-10 badge-phoenix-danger text-uppercase px-3 py-2">{{ _("Sold") }}</span>
{% elif car.status == "transfer" %}
<span class="badge badge-phoenix fs-10 badge-phoenix-info text-uppercase px-3 py-2">{{ _("Transfer") }}</span>
{% endif %}
</div>
<!-- Ready Status -->
<div class="col-md-2">
<div class="d-flex align-items-center">
<span class="fs-10 fw-light me-2">{{ _("Inventory Ready") }}</span>
{% if car.ready %}
<span class="badge bg-success rounded-circle p-1 me-2">
<span class="visually-hidden">Ready</span>
</span>
<span class="text-success fw-bold">{{ _("Yes") }}</span>
{% else %}
<span class="badge bg-danger rounded-circle p-1 me-2">
<span class="visually-hidden">Not Ready</span>
</span>
<span class="text-danger fw-bold">{{ _("No") }}</span>
{% endif %}
</div>
</div>
</div>
</div>
<!-- Action Menu -->
<div class="col-auto">
<div class="btn-reveal-trigger position-static">
<button
class="btn btn-sm dropdown-toggle dropdown-caret-none transition-none btn-reveal fs-10"
type="button"
data-bs-toggle="dropdown"
data-boundary="window"
aria-haspopup="true"
aria-expanded="false"
data-bs-reference="parent">
<span class="fas fa-ellipsis-h fs-10"></span>
</button>
<div class="dropdown-menu dropdown-menu-end py-2">
<a class="dropdown-item" href="{% 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>
<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>
</div>
</div>
</div>
</div>
</div>
</div>
{% empty %}
<div class="text-center py-5">
<div class="text-body-secondary">
<span class="fas fa-car fa-4x mb-3 opacity-50"></span>
<h4 class="text-body-secondary">{{ _("No vehicles found") }}</h4>
<p class="text-body-tertiary">{{ _("Try adjusting your search criteria or filters") }}</p>
</div>
</div>
{% endfor %}
</div>
</div>
{% if page_obj.paginator.num_pages > 1 %}

View File

@ -3,7 +3,7 @@
<div class="w-100 g-3">
<form method="post"
id="registrationForm"
action="{% url 'add_registration' car.slug %}">
action="{% url 'add_registration' request.dealer.slug car.slug %}">
{% csrf_token %}
{{ form|crispy }}
<div class="d-flex gap-1">

View File

@ -67,7 +67,7 @@
<ul>
{% for trim in model.trims %}
<li>
<a href="{% url 'car_inventory' make_id=make.slug model_id=model.slug trim_id=trim.slug %}">{{ trim.trim_name }}</a>&nbsp;-&nbsp;{% trans "Total" %}:
<a href="{% url 'car_inventory' dealer_slug=request.dealer.slug make_id=make.slug model_id=model.slug trim_id=trim.slug %}">{{ trim.trim_name }}</a>&nbsp;-&nbsp;{% trans "Total" %}:
<strong>{{ trim.total_cars }}</strong>
</li>
{% empty %}

View File

@ -14,7 +14,7 @@
<div class="row mt-4">
<div class="d-flex justify-content-between mb-2">
<h3 class="">{% trans "Bills" %}</h3>
<a href="{% url 'bill-create' entity.slug %}" class="btn btn-md btn-phoenix-primary"><i class="fa fa-plus me-2"></i>{% trans 'New Bill' %}</a>
<a href="{% url 'bill-create' request.dealer.slug entity.slug %}" class="btn btn-md btn-phoenix-primary"><i class="fa fa-plus me-2"></i>{% trans 'New Bill' %}</a>
</div>
<div class="col-12">
@ -81,7 +81,7 @@
<div class="btn-reveal-trigger position-static">
<button class="btn btn-sm dropdown-toggle dropdown-caret-none transition-none btn-reveal fs-10" type="button" data-bs-toggle="dropdown" data-boundary="window" aria-haspopup="true" aria-expanded="false" data-bs-reference="parent"><span class="fas fa-ellipsis-h fs-10"></span></button>
<div class="dropdown-menu dropdown-menu-end py-2">
<a href="{% url 'bill-detail' entity_slug=entity.slug bill_pk=bill.pk %}" class="dropdown-item text-success-dark">{% trans 'View Bill detail' %}</a>
<a href="{% url 'bill-detail' dealer_slug=request.dealer.slug entity_slug=entity.slug bill_pk=bill.pk %}" class="dropdown-item text-success-dark">{% trans 'View' %}</a>
</div>
</div>
</td>
@ -97,13 +97,13 @@
</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 %}
</div>

View File

@ -17,7 +17,7 @@
<div class="btn-group">
<button type="button" class="btn btn-sm btn-phoenix-secondary" data-bs-dismiss="modal">{% trans 'No' %}</button>
<div class="btn btn-sm btn-phoenix-danger">
<form action="{% url 'account_delete' account.pk %}" method="post">
<form action="{% url 'account_delete' request.dealer.slug account.pk %}" method="post">
{% csrf_token %}
<button type="submit" class="btn btn-sm btn-phoenix-danger">{% trans 'Yes' %}</button>
</form>
@ -99,7 +99,7 @@
<div class="btn-reveal-trigger position-static">
<button class="btn btn-sm dropdown-toggle dropdown-caret-none transition-none btn-reveal fs-10" type="button" data-bs-toggle="dropdown" data-boundary="window" aria-haspopup="true" aria-expanded="false" data-bs-reference="parent"><span class="fas fa-ellipsis-h fs-10"></span></button>
<div class="dropdown-menu dropdown-menu-end py-2">
<a class="dropdown-item text-success" href="{% url 'payment_details' tx.journal_entry.pk %}">{% trans 'View Transacrions'|capfirst %}</a>
<a class="dropdown-item" href="{% url 'payment_details' request.dealer.slug tx.journal_entry.pk %}">{% trans 'view'|capfirst %}</a>
</div>
</div>
</td>
@ -135,7 +135,7 @@
</div>
<div class="mt-3 d-flex">
<a class="btn btn-sm btn-phoenix-primary me-1" href="{% url 'account_update' account.pk %}">
<a class="btn btn-sm btn-phoenix-primary me-1" href="{% url 'account_update' request.dealer.slug account.pk %}">
<!-- <i class="bi bi-pencil-square"></i> -->
<i class="fa-solid fa-pen-to-square"></i> {{ _('Edit') }}
</a>
@ -143,7 +143,7 @@
<!-- <i class="bi bi-trash-fill"></i> -->
<i class="fa-solid fa-trash"></i> {{ _('Delete') }}
</a>
<a class="btn btn-sm btn-phoenix-secondary" href="{% url 'account_list' %}">
<a class="btn btn-sm btn-phoenix-secondary" href="{% url 'account_list' request.dealer.slug %}">
<!-- <i class="bi bi-arrow-left-square-fill"></i> -->
<i class="fa-regular fa-circle-left"></i> {% trans 'Back to List' %}
</a>

View File

@ -11,7 +11,7 @@
<div class="row mt-4">
<div class="d-flex justify-content-between mb-2">
<h3 class=""><i class="fa-solid fa-book"></i> {% trans "Accounts" %}</h3>
<a href="{% url 'account_create' %}" class="btn btn-md btn-phoenix-primary"><i class="fa fa-plus me-2"></i>{% trans 'New Account' %}</a>
<a href="{% url 'account_create' request.dealer.slug %}" class="btn btn-md btn-phoenix-primary"><i class="fa fa-plus me-2"></i>{% trans 'New Account' %}</a>
</div>
<!-- Account Type Tabs -->

View File

@ -38,8 +38,8 @@
<div class="btn-reveal-trigger position-static">
<button class="btn btn-sm dropdown-toggle dropdown-caret-none transition-none btn-reveal fs-10" type="button" data-bs-toggle="dropdown" data-boundary="window" aria-haspopup="true" aria-expanded="false" data-bs-reference="parent"><span class="fas fa-ellipsis-h fs-10"></span></button>
<div class="dropdown-menu dropdown-menu-end py-2">
<a href="{% url 'account_detail' account.uuid %}" class="dropdown-item text-success-dark">
{% trans "View Journal Entries" %}
<a href="{% url 'account_detail' request.dealer.slug account.uuid %}" class="dropdown-item text-success-dark">
{% trans "View" %}
</a>
<div class="dropdown-divider"></div>
<button class="dropdown-item text-danger" data-bs-toggle="modal" data-bs-target="#deleteModal">{% trans "Delete" %}</button>

View File

@ -88,11 +88,11 @@
{% endif %}
{% for bill in bills %}
<div class="col">
{% include 'django_ledger/bills/includes/card_bill.html' with bill=bill entity_slug=entity.slug style='dashboard' %}
{% include 'django_ledger/bills/includes/card_bill.html' with dealer_slug=request.dealer.slug bill=bill entity_slug=entity.slug style='dashboard' %}
</div>
{% endfor %}
<div class="col">
{% include 'django_ledger/bills/includes/card_bill.html' with create_bill=True entity_slug=entity.slug style='dashboard' %}
{% include 'django_ledger/bills/includes/card_bill.html' with dealer_slug=request.dealer.slug create_bill=True entity_slug=entity.slug style='dashboard' %}
</div>
</div>
</div>

View File

@ -56,7 +56,7 @@
<div class="mt-3">
<button type="submit" class="btn btn-phoenix-primary">Save</button>
<a href="{% url 'purchase_order_list' %}" class="btn btn-phoenix-secondary">Cancel</a>
<a href="{% url 'purchase_order_list' request.dealer.slug %}" class="btn btn-phoenix-secondary">Cancel</a>
</div>
</form>
</div>

View File

@ -162,7 +162,7 @@
<div class="card-footer bg-light">
<div class="d-flex flex-wrap gap-2 justify-content-between">
<a href="{% url 'purchase_order_update' entity_slug=entity_slug po_pk=po_model.pk %}"
<a href="{% url 'purchase_order_update' dealer_slug=request.dealer.slug entity_slug=entity_slug po_pk=po_model.pk %}"
class="btn btn-phoenix-primary">
<i class="fas fa-edit me-2"></i>{% trans 'Update' %}
</a>
@ -171,28 +171,28 @@
{# Status Action Buttons #}
{% if po_model.can_draft %}
<button class="btn btn-phoenix-secondary"
onclick="showPOModal('Draft PO', '{% url 'po-action-mark-as-draft' entity_slug po_model.pk %}', 'Mark As Draft')">
onclick="showPOModal('Draft PO', '{% url 'po-action-mark-as-draft' request.dealer.slug entity_slug po_model.pk %}', 'Mark As Draft')">
<i class="fas fa-file me-2"></i>{% trans 'Mark as Draft' %}
</button>
{% endif %}
{% if po_model.can_review %}
<button class="btn btn-phoenix-warning"
onclick="showPOModal('Review PO', '{% url 'po-action-mark-as-review' entity_slug po_model.pk %}', 'Mark As Review')">
onclick="showPOModal('Review PO', '{% url 'po-action-mark-as-review' request.dealer.slug entity_slug po_model.pk %}', 'Mark As Review')">
<i class="fas fa-search me-2"></i>{% trans 'Mark as Review' %}
</button>
{% endif %}
{% if po_model.can_approve %}
<button class="btn btn-phoenix-success"
onclick="showPOModal('Approve PO', '{% url 'po-action-mark-as-approved' entity_slug po_model.pk %}', 'Mark As Approved')">
onclick="showPOModal('Approve PO', '{% url 'po-action-mark-as-approved' request.dealer.slug entity_slug po_model.pk %}', 'Mark As Approved')">
<i class="fas fa-check-circle me-2"></i>{% trans 'Mark as Approved' %}
</button>
{% endif %}
{% if po_model.can_fulfill %}
<button class="btn btn-phoenix-primary"
onclick="showPOModal('Fulfill PO', '{% url 'po-action-mark-as-fulfilled' entity_slug po_model.pk %}', 'Mark As Fulfilled')">
onclick="showPOModal('Fulfill PO', '{% url 'po-action-mark-as-fulfilled' request.dealer.slug entity_slug po_model.pk %}', 'Mark As Fulfilled')">
<i class="fas fa-truck me-2"></i>{% trans 'Mark as Fulfilled' %}
</button>
@ -201,21 +201,21 @@
{# Danger Action Buttons #}
{% if po_model.can_delete %}
<button class="btn btn-outline-danger"
onclick="showPOModal('Cancel PO', '{% url 'po-delete' entity_slug po_model.pk %}', 'Mark As Cancelled')">
onclick="showPOModal('Delete PO', '{% url 'po-delete' request.dealer.slug entity_slug po_model.pk %}', 'Delete')">
<i class="fas fa-ban me-2"></i>{% trans 'Delete' %}
</button>
{% endif %}
{% if po_model.can_void %}
<button class="btn btn-outline-danger"
onclick="showPOModal('Void PO', '{% url 'po-action-mark-as-void' entity_slug po_model.pk %}', 'Mark As Void')">
onclick="showPOModal('Void PO', '{% url 'po-action-mark-as-void' request.dealer.slug entity_slug po_model.pk %}', 'Mark As Void')">
<i class="fas fa-times-circle me-2"></i>{% trans 'Void' %}
</button>
{% endif %}
{% if po_model.can_cancel %}
<button class="btn btn-outline-danger"
onclick="showPOModal('Cancel PO', '{% url 'po-action-mark-as-canceled' entity_slug po_model.pk %}', 'Mark As Cancelled')">
onclick="showPOModal('Cancel PO', '{% url 'po-action-mark-as-canceled' request.dealer.slug entity_slug po_model.pk %}', 'Mark As Cancelled')">
<i class="fas fa-ban me-2"></i>{% trans 'Cancel' %}
</button>
@ -228,7 +228,7 @@
{% else %}
<div class="card border-0 shadow-sm text-center py-5">
<div class="card-body">
<a href="{% url 'purchase_order_create' %}" class="text-decoration-none">
<a href="{% url 'purchase_order_create' request.dealer.slug %}" class="text-decoration-none">
<span class="text-muted mb-3 d-inline-block">{% icon "ic:baseline-add-circle-outline" 48 %}</span>
<h3 class="h4 text-muted">{% trans 'New Purchase Order' %}</h3>
</a>

View File

@ -3,7 +3,7 @@
{% block content %}
<form action="{% url 'inventory_item_create' po_model.pk %}" method="post">
<form action="{% url 'inventory_item_create' request.dealer.slug po_model.pk %}" method="post">
{% csrf_token %}
{% include "purchase_orders/partials/po-select.html" with name="make" target="model" data=make_data pk=po_model.pk %}
{% include "purchase_orders/partials/po-select.html" with name="model" target="serie" data=model_data pk=po_model.pk %}

View File

@ -4,7 +4,7 @@
{% load custom_filters %}
{% load widget_tweaks %}
<form action="{% url 'purchase_order_update_items' entity_slug=entity_slug po_pk=po_model.uuid %}"
<form action="{% url 'purchase_order_update_items' dealer_slug=dealer_slug entity_slug=entity_slug po_pk=po_model.uuid %}"
method="post">
<div class="row g-3">
<div class="col-12">
@ -60,7 +60,7 @@
{{ f.create_bill|add_class:"form-check-input" }}
{% elif f.instance.bill_model %}
<a class="btn btn-sm btn-phoenix-secondary"
href="{% url 'bill-detail' entity_slug=entity_slug bill_pk=f.instance.bill_model_id %}">
href="{% url 'bill-detail' dealer_slug=dealer_slug entity_slug=entity_slug bill_pk=f.instance.bill_model_id %}">
{% trans 'View Bill' %}
</a>
{% endif %}

View File

@ -3,7 +3,7 @@
<select class="form-control"
name="{{name}}" id="{{name}}"
{% if name != "trim" %}
hx-get="{% url 'inventory_items_filter' %}"
hx-get="{% url 'inventory_items_filter' request.dealer.slug %}"
hx-target="#form-{{target}}"
hx-select="#form-{{target}}"
hx-swap="outerHTML"

View File

@ -6,7 +6,7 @@
{% block content %}
<div class="row justify-content-center">
<div class="col-md-6">
<form action="{% url 'po-delete' entity_slug=view.kwargs.entity_slug po_pk=po_model.uuid %}"
<form action="{% url 'po-delete' dealer_slug=request.dealer.slug entity_slug=view.kwargs.entity_slug po_pk=po_model.uuid %}"
method="post">
{% csrf_token %}
<div class="card shadow">
@ -15,10 +15,10 @@
Purchase Order {{ po_model.po_number }}?</h2>
<p class="card-text text-muted mb-4">All transactions associated with this Purchase Order will be deleted.
If you want to void the PO instead, <a href="{% url 'purchase_order_detail' entity_slug=view.kwargs.entity_slug po_pk=po_model.uuid %}" class="text-decoration-none">click here</a></p>
If you want to void the PO instead, <a href="{% url 'purchase_order_detail' dealer_slug=request.dealer.slug pk=po_model.uuid %}" class="text-decoration-none">click here</a></p>
<div class="d-flex justify-content-center gap-3 mt-4">
<a href="{% url 'purchase_order_update' entity_slug=view.kwargs.entity_slug po_pk=po_model.uuid %}"
<a href="{% url 'purchase_order_update' dealer_slug=request.dealer.slug entity_slug=view.kwargs.entity_slug po_pk=po_model.uuid %}"
class="btn btn-phoenix-primary px-4">{% trans 'Go Back' %}</a>
<button type="submit" class="btn btn-phoenix-danger px-4">{% trans 'Delete' %}</button>
</div>

View File

@ -7,19 +7,28 @@
{% block content %}
<div class="container-fluid mt-4">
<div class="row g-1">
<div class="col-lg-12">
<div class="d-flex flex-column gap-3">
<!-- PO Card -->
<div class="card">
<div class="card-body">
{% include 'purchase_orders/includes/card_po.html' with po_model=po_model entity_slug=entity_slug style='po-detail' %}
{% include 'purchase_orders/includes/card_po.html' with dealer_slug=request.dealer.slug po_model=po_model entity_slug=entity_slug style='po-detail' %}
</div>
</div>
<!-- PO Stats Card-->
<div class="card mb-4">
<!-- PO List Button -->
<a class="btn btn-phoenix-primary w-100 py-2"
href="{% url 'purchase_order_list' request.dealer.slug %}">
<i class="fas fa-list me-2"></i>{% trans 'PO List' %}
</a>
</div>
</div>
<!-- Main Content -->
<div class="col-lg-8">
<!-- Stats Cards -->
<div class="card mb-4">
<div class="card-body">
<div class="row text-center">
<div class="col-md-6 border-end">

View File

@ -19,9 +19,9 @@
{{ _("Purchase Orders") |capfirst }}
</h2>
<div>
<a href="{% url 'purchase_order_create' %}"
<a href="{% url 'purchase_order_create' request.dealer.slug %}"
class="btn btn-md btn-phoenix-primary"><i class="fa fa-plus me-2"></i>{{ _("Create New PO") }}</a>
<a href="{% url 'inventory_item_create' %}?for_po=1"
<a href="{% url 'inventory_item_create' request.dealer.slug %}?for_po=1"
class="btn btn-md btn-phoenix-primary"><i class="fa fa-plus me-2"></i>{{ _("Create Inventory Item for PO") }}</a>
</div>
</div>
@ -67,8 +67,8 @@
<div class="btn-reveal-trigger position-static">
<button class="btn btn-sm dropdown-toggle dropdown-caret-none transition-none btn-reveal fs-10" type="button" data-bs-toggle="dropdown" data-boundary="window" aria-haspopup="true" aria-expanded="false" data-bs-reference="parent"><span class="fas fa-ellipsis-h fs-10"></span></button>
<div class="dropdown-menu dropdown-menu-end py-2">
<a href="{% url 'purchase_order_detail' po.pk %}" class="dropdown-item text-success-dark">{% trans 'PO Detail' %}</a>
<a href="{% url 'view_items_inventory' entity_slug=entity_slug po_pk=po.pk %}" class="dropdown-item text-success-dark">{% trans 'View Inventory Items' %}</a>
<a href="{% url 'purchase_order_detail' request.dealer.slug po.pk %}" class="dropdown-item text-success-dark">{% trans 'Detail' %}</a>
<a href="{% url 'view_items_inventory' dealer_slug=request.dealer.slug entity_slug=entity_slug po_pk=po.pk %}" class="dropdown-item text-success-dark">{% trans 'View Inventory Items' %}</a>
</div>
</div>
</td>
@ -83,13 +83,13 @@
</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 %}
</div>

View File

@ -12,12 +12,12 @@
<div class="row g-4">
<div class="col-12">
{% include 'purchase_orders/includes/card_po.html' with style='po-detail' po_model=po_model entity_slug=entity_slug %}
{% include 'purchase_orders/includes/card_po.html' with style='po-detail' dealer_slug=request.dealer.slug po_model=po_model entity_slug=entity_slug %}
</div>
<div class="col-12">
<div class="card shadow-sm">
<div class="card-body">
<form action="{% url 'purchase_order_update' entity_slug=view.kwargs.entity_slug po_pk=po_model.uuid %}"
<form action="{% url 'purchase_order_update' dealer_slug=request.dealer.slug entity_slug=view.kwargs.entity_slug po_pk=po_model.uuid %}"
method="post">
<div class="row g-3">
<div class="col-12">
@ -26,9 +26,9 @@
<button type="submit"
class="btn btn-phoenix-success w-100 my-2">{% trans 'Save PO' %}
</button>
<a href="{% url 'purchase_order_detail' po_model.uuid %}"
<a href="{% url 'purchase_order_detail' request.dealer.slug po_model.uuid %}"
class="btn btn-phoenix-secondary w-100 my-2">{% trans 'Back to PO Detail' %}</a>
<a href="{% url 'purchase_order_list' %}"
<a href="{% url 'purchase_order_list' request.dealer.slug %}"
class="btn btn-phoenix-info
info w-100 my-2">{% trans 'PO List' %}</a>
</div>

View File

@ -28,7 +28,7 @@
</td>
<td class="has-text-centered">{% if item.bill_model_id %}
<a class="is-small is-info button"
href="{% url 'bill-detail' entity_slug=entity_slug bill_pk=item.bill_model_id %}">View
href="{% url 'bill-detail' dealer_slug=dealer_slug entity_slug=entity_slug bill_pk=item.bill_model_id %}">View
Bill</a>{% endif %}
</td>
</tr>

View File

@ -16,7 +16,7 @@
<p>{% trans "Are you sure you want to Cancel this Estimate?" %}</p>
</div>
<div class="modal-footer flex justify-content-center border-top-0">
<a type="button" class="btn btn-sm btn-phoenix-danger w-100" href="{% url 'estimate_mark_as' estimate.pk %}?mark=canceled">
<a type="button" class="btn btn-sm btn-phoenix-danger w-100" href="{% url 'estimate_mark_as' request.dealer.slug estimate.pk %}?mark=canceled">
<i class="fa-solid fa-circle-check"></i> {% trans "Yes" %}
</a>
</div>
@ -40,7 +40,7 @@
<div class="modal-body">
{% trans 'Are you sure ?' %}
<div class="modal-footer flex justify-content-center border-top-0">
<form id="confirmForm" method="POST" action="{% url 'estimate_mark_as' estimate.pk %}?mark=approved" class="form">
<form id="confirmForm" method="POST" action="{% url 'estimate_mark_as' request.dealer.slug estimate.pk %}?mark=approved" class="form">
{% csrf_token %}
<div class="container-fluid m-0 p-0">
<button type="button" class="btn btn-phoenix-danger btn-sm" data-bs-dismiss="modal"><i class="fa-solid fa-ban"></i> {% trans 'No' %}</button>
@ -74,32 +74,28 @@
</div>
</div>
<div class="d-flex align-items-center gap-2">
{% if estimate.status == 'draft' %}
<a href="{% url 'send_email' estimate.pk %}" class="btn btn-phoenix-primary me-2"><span class="fa-regular fa-paper-plane me-sm-2"></span><span class="d-none d-sm-inline-block">{% trans 'Send Quotation' %}</span></a>
{% if estimate.invoicemodel_set.first %}
<a href="{% url 'invoice_detail' request.dealer.slug estimate.invoicemodel_set.first.pk %}" class="btn btn-phoenix-primary btn-sm" type="button"><i class="fa-solid fa-receipt"></i>
{{ _("View Invoice")}}</a>
<button class="btn btn-phoenix-primary" data-bs-toggle="modal" data-bs-target="#POModal"><span class="d-none d-sm-inline-block"><i class="fa-solid fa-receipt"></i> {% trans 'View Purchase Order' %}</span></button>
{% endif %}
{% if estimate.status == 'draft' %}
<a href="{% url 'send_email' request.dealer.slug estimate.pk %}" class="btn btn-phoenix-primary me-2"><span class="fa-regular fa-paper-plane me-sm-2"></span><span class="d-none d-sm-inline-block">{% trans 'Send Quotation' %}</span></a>
<button id="mark_as_sent_estimate" class="btn btn-phoenix-secondary" onclick="setFormAction('review')" data-bs-toggle="modal" data-bs-target="#confirmModal"><span class="d-none d-sm-inline-block"><i class="fa-solid fa-check-double"></i> {% trans 'Mark As Sent' %}</span></button>
{% elif estimate.status == 'in_review' %}
<button id="accept_estimate" onclick="setFormAction('approved')" class="btn btn-phoenix-secondary" data-bs-toggle="modal" data-bs-target="#confirmModal"><span class="d-none d-sm-inline-block"><i class="fa-solid fa-check-double"></i> {% trans 'Mark As Accept' %}</span></button>
{% elif estimate.status == 'approved' %}
{% if estimate.sale_orders.first %}
<!--if there is a sales order for an estimate-->
<!--View sales Order-->
<a href="{% url 'order_detail' estimate.sale_orders.first.pk %}" class="btn btn-phoenix-primary btn-sm" type="button"><i class="fa-solid fa-cart-shopping"></i>
{{ _("View Sales Order")}}
</a>
{% if estimate.sale_orders.first.invoice %}
<a href="{% url 'invoice_detail' estimate.invoicemodel_set.first.pk %}" class="btn btn-phoenix-primary btn-sm" type="button"><i class="fa-solid fa-receipt"></i>
{{ _("View Invoice")}}</a>
{% else %}
<!--if there is no invoice for a sale order create invoice -->
<a href="{% url 'invoice_create' estimate.pk %}" class="btn btn-phoenix-primary"><span class="d-none d-sm-inline-block"><i class="fa-solid fa-receipt"></i> {% trans 'Create Invoice' %}</span></a>
{% endif %}
<a href="{% url 'invoice_create' request.dealer.slug estimate.pk %}" class="btn btn-phoenix-primary"><span class="d-none d-sm-inline-block"><i class="fa-solid fa-receipt"></i> {% trans 'Create Invoice' %}</span></a>
<a href="{% url 'preview_sale_order' request.dealer.slug estimate.pk %}" class="btn btn-phoenix-primary"><span class="d-none d-sm-inline-block">{{ _("Preview Sale Order") }}</span></a>
{% else %}
<!--if no sales order for the estimate Create sales order-->
<a href="{% url 'create_sale_order' estimate.pk %}" class="btn btn-phoenix-primary"><span class="d-none d-sm-inline-block"><i class="fa-solid fa-file-import"></i> {% trans 'Create Sale Order' %}</span></a>
<a href="{% url 'create_sale_order' request.dealer.slug estimate.pk %}" class="btn btn-phoenix-primary"><span class="d-none d-sm-inline-block"><i class="fa-solid fa-file-import"></i> {% trans 'Create Sale Order' %}</span></a>
{% comment %} {% endcomment %}
{% endif %}
{% elif estimate.status == 'in_review' %}
<a href="{% url 'estimate_preview' estimate.pk %}" class="btn btn-phoenix-primary"><span class="d-none d-sm-inline-block"><i class="fa-regular fa-eye"></i> {% trans 'Preview' %}</span></a>
<a href="{% url 'estimate_preview' request.dealer.slug estimate.pk %}" class="btn btn-phoenix-primary"><span class="d-none d-sm-inline-block"><i class="fa-regular fa-eye"></i> {% trans 'Preview' %}</span></a>
{% endif %}
{% if estimate.can_cancel %}
{% if perms.django_ledger.change_estimatemodel %}
@ -144,7 +140,7 @@
</div>
</div>
</div>
<div class="px-0">
<div class="table-responsive scrollbar">
<table id="estimate-table" class="table fs-9 text-body mb-0">
@ -245,7 +241,7 @@
// Get the form element
const form = document.getElementById('confirmForm');
// Set the form action with the query parameter
form.action = "{% url 'estimate_mark_as' estimate.pk %}?mark=" + action;
form.action = "{% url 'estimate_mark_as' request.dealer.slug estimate.pk %}?mark=" + action;
}
</script>

View File

@ -17,14 +17,14 @@
{% if not items %}
<div class="alert alert-outline-warning d-flex align-items-center" role="alert">
<i class="fa-solid fa-circle-info fs-6"></i>
<p class="mb-0 flex-1">{{ _("Please add at least one car before creating a quotation.") }}<a class="ms-3 text-body-primary fs-9" href="{% url 'car_add' %}"> {{ _("Add Car") }} </a></p>
<p class="mb-0 flex-1">{{ _("Please add at least one car before creating a quotation.") }}<a class="ms-3 text-body-primary fs-9" href="{% url 'car_add' request.dealer.slug %}"> {{ _("Add Car") }} </a></p>
<button class="btn-close" type="button" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
{% endif %}
{% if not customer_count %}
<div class="alert alert-outline-warning d-flex align-items-center" role="alert">
<i class="fa-solid fa-circle-info fs-6"></i>&nbsp;&nbsp;
<p class="mb-0 flex-1"> {{ _("Please add at least one customer before creating a quotation.") }}<a class="ms-3 text-body-primary fs-9" href="{% url 'customer_create' %}"> {{ _("Add Customer") }} </a></p>
<p class="mb-0 flex-1"> {{ _("Please add at least one customer before creating a quotation.") }}<a class="ms-3 text-body-primary fs-9" href="{% url 'customer_create' request.dealer.slug %}"> {{ _("Add Customer") }} </a></p>
<button class="btn-close" type="button" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
{% endif %}
@ -163,7 +163,7 @@
try {
// Send data to the server using fetch
const response = await fetch("{% url 'estimate_create' %}", {
const response = await fetch("{% url 'estimate_create' request.dealer.slug %}", {
method: 'POST',
headers: {
'X-CSRFToken': formData.csrfmiddlewaretoken,

View File

@ -44,7 +44,7 @@
<td class="align-middle product white-space-nowrap">{{ estimate.get_status_action_date }}</td>
<td class="align-middle product white-space-nowrap">{{ estimate.created }}</td>
<td class="align-middle product white-space-nowrap">
<a href="{% url 'estimate_detail' estimate.pk %}"
<a href="{% url 'estimate_detail' request.dealer.slug estimate.pk %}"
class="btn btn-sm btn-phoenix-success">
<i class="fa-regular fa-eye me-1"></i>
{% trans "view"|capfirst %}

View File

@ -23,7 +23,7 @@
</div>
<div class="d-flex justify-content-between align-items-center">
<div class="d-flex">
<a href="{% url 'estimate_detail' estimate.pk %}" class="btn btn-sm btn-phoenix-secondary text-body fs-10 me-1">{{ _("Cancel") }}</a>
<a href="{% url 'estimate_detail' request.dealer.slug estimate.pk %}" class="btn btn-sm btn-phoenix-secondary text-body fs-10 me-1">{{ _("Cancel") }}</a>
<button class="btn btn-sm btn-phoenix-primary fs-10" type="submit">{{ _("Send") }}<span class="fa-solid fa-paper-plane ms-1"></span></button>
</div>
</div>

View File

@ -172,7 +172,7 @@
<!-- Form Actions -->
<div class="form-actions mt-4">
<div class="d-flex justify-content-between">
<a href="{% url 'estimate_detail' estimate.pk %}" type="button" class="btn btn-phoenix-secondary">
<a href="{% url 'estimate_detail' request.dealer.slug estimate.pk %}" type="button" class="btn btn-phoenix-secondary">
<i class="fas fa-times me-2"></i> Cancel
</a>
<div>

View File

@ -26,7 +26,7 @@
class="btn btn-sm btn-phoenix-danger"
data-bs-dismiss="modal"><i class="fa-solid fa-ban"></i> {% trans 'No' %}
</button>
<form id="confirmForm" method="POST" action="{% url 'invoice_mark_as' invoice.pk %}?mark=accept" class="d-inline">
<form id="confirmForm" method="POST" action="{% url 'invoice_mark_as' request.dealer.slug invoice.pk %}?mark=accept" class="d-inline">
{% csrf_token %}
<button type="submit" class="btn btn-phoenix-success btn-sm"><i class="fa-solid fa-circle-check"></i> {% trans "Yes" %}</button>
</form>
@ -35,7 +35,7 @@
</div>
</div>
</div>
<!-- ============================================-->
<!-- ============================================-->
<div class="modal fade" id="mark_as_paid_Modal" tabindex="-1" aria-labelledby="confirmModalLabel" aria-hidden="true">
<div class="modal-dialog modal-sm">
<div class="modal-content">
@ -51,7 +51,7 @@
data-bs-dismiss="modal">
<i class="fa-solid fa-ban"></i> {% trans 'No' %}
</button>
<form id="confirmForm" method="POST" action="{% url 'payment_mark_as_paid' invoice.pk %}" class="d-inline">
<form id="confirmForm" method="POST" action="{% url 'payment_mark_as_paid' request.dealer.slug invoice.pk %}" class="d-inline">
{% csrf_token %}
<button type="submit" class="btn btn-phoenix-success btn-sm"><i class="fa-solid fa-circle-check"></i> {% trans "Yes" %}</button>
</form>
@ -60,7 +60,7 @@
</div>
</div>
</div>
<!-- ============================================-->
<!-- ============================================-->
<!-- <section> begin ============================-->
<section class="pt-5 pb-9 bg-body-emphasis dark__bg-gray-1200 border-top">
<div class="row-small mt-3 mx-3">
@ -82,40 +82,21 @@
</div>
</div>
<div class="d-flex align-items-center gap-2">
{# Accept Invoice Button #}
{% if invoice.invoice_status == 'in_review' %}
<button id="accept_invoice" class="btn btn-phoenix-secondary btn-sm" data-bs-toggle="modal" data-bs-target="#confirmModal">
<i class="fa-solid fa-check-double me-1 me-sm-0 me-md-1"></i> {# Icon always visible, adjusted margin #}
<span class="d-none d-sm-inline-block">{% trans 'Accept' %}</span> {# Text hidden on xs, shown on sm+ #}
</button>
{% endif %}
{# Record Payment Button #}
{% if invoice.invoice_status == 'approved' %}
<a href="{% url 'payment_create' invoice.pk %}" class="btn btn-phoenix-success btn-sm">
<i class="fa-solid fa-money-bill me-1 me-sm-0 me-md-1"></i> {# Icon always visible, adjusted margin #}
<span class="d-none d-sm-inline-block">{% trans 'Record Payment' %}</span> {# Text hidden on xs, shown on sm+ #}
</a>
{% endif %}
{# Mark as Paid Button #}
{% if not invoice.is_paid %}
<button {% if invoice.is_review or invoice.amount_paid|to_int < invoice.amount_due|to_int %}disabled{% endif %} id="mark_invoice_as_paid" class="btn btn-phoenix-secondary btn-sm" data-bs-toggle="modal" data-bs-target="#mark_as_paid_Modal">
<span class="icon-saudi_riyal me-1 me-sm-0 me-md-1"></span> {# Icon always visible, adjusted margin #}
<span class="d-none d-sm-inline-block">{% trans 'Mark as Paid' %}</span> {# Text hidden on xs, shown on sm+ #}
</button>
{% endif %}
{# Preview Button #}
<a href="{% url 'invoice_preview' invoice.pk %}" class="btn btn-phoenix-primary btn-sm">
<i class="fa-regular fa-eye me-1 me-sm-0 me-md-1"></i> {# Icon always visible, adjusted margin #}
<span class="d-none d-sm-inline-block">{% trans 'Preview' %}</span> {# Text hidden on xs, shown on sm+ #}
</a>
</div>
{% if invoice.invoice_status == 'in_review' %}
<button id="accept_invoice" class="btn btn-phoenix-secondary" data-bs-toggle="modal" data-bs-target="#confirmModal"><span class="d-none d-sm-inline-block"><i class="fa-solid fa-check-double"></i> {% trans 'Accept' %}</span></button>
{% endif %}
{% if invoice.invoice_status == 'approved' %}
<a href="{% url 'payment_create' request.user.dealer.slug invoice.pk %}" class="btn btn-phoenix-success"><span class="d-none d-sm-inline-block"><i class="fa-solid fa-money-bill"></i> {% trans 'Record Payment' %}</span></a>
{% endif %}
{% if not invoice.is_paid %}
<button {% if invoice.is_review or invoice.amount_paid|to_int < invoice.amount_due|to_int %}disabled{% endif %} id="mark_invoice_as_paid" class="btn btn-phoenix-secondary" data-bs-toggle="modal" data-bs-target="#mark_as_paid_Modal"><span class="d-none d-sm-inline-block"><span class="icon-saudi_riyal"></span> {% trans 'Mark as Paid' %}</span></button>
{% endif %}
<a href="{% url 'invoice_preview' request.user.dealer.slug invoice.pk %}" class="btn btn-phoenix-primary"><span class="d-none d-sm-inline-block"><i class="fa-regular fa-eye"></i> {% trans 'Preview' %}</span></a>
</div>
</div>
{{invoice.amount_owned}}
<!-- ============================================-->
<!-- ============================================-->
<div class="card mb-5 {% if invoice.is_review %}disabled{% endif %}">
<div class="card-body">
<div class="row g-4 g-xl-1 g-xxl-3 justify-content-between">
@ -190,7 +171,7 @@
<!-- <section> begin ============================-->
<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="col mb-2">
<h6 class="mb-0 me-3"><i class="fa-solid fa-hashtag"></i> {% trans "Invoice Number" %} :</h6>
@ -223,7 +204,7 @@
<span class="badge text-bg-success">{% trans "Paid" %}</span>
{% endif %}
</div>
</div>
</div>
</div>

View File

@ -4,7 +4,6 @@
{% block title %}{{ _("Invoices") }}{% endblock title %}
{% block content %}
<div class="row mt-4">
<h3 class="mb-3"><i class="fa-solid fa-receipt"></i> {% trans "Invoices" %}</h3>
@ -55,7 +54,7 @@
</td>
<td class="align-middle product white-space-nowrap">{{ invoice.created }}</td>
<td class="align-middle product white-space-nowrap">
<a href="{% url 'invoice_detail' invoice.pk %}"
<a href="{% url 'invoice_detail' request.dealer.slug invoice.pk %}"
class="btn btn-sm btn-phoenix-success">
<i class="fa-regular fa-eye me-1"></i>
{% trans "View" %}

View File

@ -29,7 +29,7 @@
<td class="align-middle product white-space-nowrap">{{ journal. }}</td>
<td class="align-middle product white-space-nowrap">{{ journal. }}</td>
<td class="text-center">
<a href="{% url 'invoice_detail' invoice.pk %}"
<a href="{% url 'invoice_detail' request.dealer.slug invoice.pk %}"
class="btn btn-sm btn-phoenix-success">
{% trans "view" %}
</a>

View File

@ -23,7 +23,7 @@
{% endif %}
<div class="card-body">
{% if model %}
<form method="post" action="{% url 'payment_create' pk=model.pk %}">
<form method="post" action="{% url 'payment_create' request.dealer.slug model.pk %}">
{% endif %}
{% csrf_token %}
{{ form|crispy }}

View File

@ -30,11 +30,11 @@
<td class="align-middle product white-space-nowrap py-0">{{ journal.je_number }}</td>
{% if journal.ledger.invoicemodel %}
<td class="align-middle product white-space-nowrap py-0">
<a href="{% url 'invoice_detail' journal.ledger.invoicemodel.pk %}"><i class="fa-solid fa-receipt"></i> {{ journal.ledger.invoicemodel }}</a>
<a href="{% url 'invoice_detail' request.dealer.slug journal.ledger.invoicemodel.pk %}"><i class="fa-solid fa-receipt"></i> {{ journal.ledger.invoicemodel }}</a>
</td>
{% elif journal.ledger.billmodel %}
<td class="align-middle product white-space-nowrap py-0">
<a href="{% url 'bill_detail' journal.ledger.billmodel.pk %}"><i class="fa-solid fa-money-bill"></i> {{ journal.ledger.billmodel }}</a>
<a href="{% url 'bill-detail' request.dealer.slug request.dealer.entity.slug journal.ledger.billmodel.pk %}"><i class="fa-solid fa-money-bill"></i> {{ journal.ledger.billmodel }}</a>
</td>
{% else %}
<td class="align-middle product white-space-nowrap py-0"></td>
@ -42,7 +42,7 @@
<td class="align-middle product white-space-nowrap py-0">{{ journal.timestamp }}</td>
<td class="align-middle product white-space-nowrap py-0">{{ journal.description }}</td>
<td class="align-middle product white-space-nowrap">
<a href="{% url 'payment_details' journal.pk %}" class="btn btn-sm btn-phoenix-primary"><i class="fa-solid fa-eye me-1"></i> {% trans "View Tranactions"|capfirst %}</a>
<a href="{% url 'payment_details' request.dealer.slug journal.pk %}" class="btn btn-sm btn-phoenix-primary"><i class="fa-solid fa-eye me-1"></i> {% trans "View Tranactions"|capfirst %}</a>
</td>
</tr>
{% empty %}

View File

@ -253,13 +253,13 @@
<!-- Add links to view full estimate and invoice if they exist -->
{% if sale_order.estimate %}
<a href="{% url 'estimate_detail' sale_order.estimate.pk %}" class="btn btn-phoenix-info ms-2">
<a href="{% url 'estimate_detail' request.dealer.slug sale_order.estimate.pk %}" class="btn btn-info ms-2">
{% trans "View Full Estimate" %}
</a>
{% endif %}
{% if sale_order.invoice %}
<a href="{% url 'invoice_detail' sale_order.invoice.pk %}" class="btn btn-phoenix-info ms-2">
<a href="{% url 'invoice_detail' request.dealer.slug sale_order.invoice.pk %}" class="btn btn-info ms-2">
{% trans "View Full Invoice" %}
</a>
{% endif %}

View File

@ -47,7 +47,7 @@
<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' %}" hx-trigger='keyup changed delay:500ms' hx-target='.table-responsive' hx-select='.table-responsive' hx-swap="innerHTML show:window:top" hx-indicator=".htmx-indicator"
<input class="form-control search-input search" name='search' type="search" placeholder="{{ _("Search") }}" aria-label="Search" hx-get="{% url 'car_list' 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()"
/>
@ -58,7 +58,7 @@
</div>
</div>
<div class="d-flex align-items-center d-none filter">
<select hx-get="{% url 'car_list' %}" name="make" hx-target='.model-select' hx-select='.model-select' hx-swap="outerHTML show:window:top" hx-indicator=".htmx-indicator" class="form-select make" aria-label="Default select example"
<select hx-get="{% url 'car_list' request.dealer.slug %}" name="make" hx-target='.model-select' hx-select='.model-select' hx-swap="outerHTML show:window:top" hx-indicator=".htmx-indicator" class="form-select make" aria-label="Default select example"
hx-on::before-request="filter_before_request()"
hx-on::after-request="filter_after_request()"
>
@ -67,7 +67,7 @@
<option value="{{ m.pk }}">{{ m.name }}</option>
{% endfor %}
</select>
<select hx-get="{% url 'car_list' %}" hx-include=".make" name="model" hx-target='.year' hx-select='.year' hx-swap="outerHTML show:window:top" hx-indicator=".htmx-indicator" class="form-select model-select" aria-label="Default select example"
<select hx-get="{% url 'car_list' request.dealer.slug %}" hx-include=".make" name="model" hx-target='.year' hx-select='.year' hx-swap="outerHTML show:window:top" hx-indicator=".htmx-indicator" class="form-select model-select" aria-label="Default select example"
hx-on::before-request="filter_before_request()"
hx-on::after-request="filter_after_request()"
>
@ -89,7 +89,7 @@
<option value="sold">Sold</option>
<option value="transfer">Transfer</option>
</select>
<button id="search" hx-get="{% url 'car_list' %}" hx-include=".make,.model,.year,.car_status" hx-indicator=".htmx-indicator" hx-target='.table-responsive' hx-select='.table-responsive' hx-swap="outerHTML show:window:top" class="btn btn-sm btn-phoenix-primary"
<button id="search" hx-get="{% url 'car_list' request.dealer.slug %}" hx-include=".make,.model,.year,.car_status" hx-indicator=".htmx-indicator" hx-target='.table-responsive' hx-select='.table-responsive' hx-swap="outerHTML show:window:top" class="btn btn-sm btn-phoenix-primary"
hx-on::before-request="filter_before_request()"
hx-on::after-request="filter_after_request()">Search</button>
</div>
@ -149,7 +149,7 @@
<td class="align-middle white-space-nowrap quotation">
{% if tx.estimate %}
<p class="fw-bo text-body fs-9 mb-0">
<a href="{% url 'estimate_detail' tx.estimate.uuid %}">
<a href="{% url 'estimate_detail' request.dealer.slug tx.estimate.uuid %}">
{{tx.estimate.estimate_number}}
</a><br>
{% if tx.estimate.status == "draft" %}
@ -167,7 +167,7 @@
<td class="align-middle white-space-nowrap invoice">
{% if tx.invoice %}
<p class="fw-bo text-body fs-9 mb-0">
<a href="{% url 'invoice_detail' tx.invoice.uuid %}">
<a href="{% url 'invoice_detail' request.dealer.slug tx.invoice.uuid %}">
{{tx.invoice.invoice_number}}
</a><br>
{% if tx.invoice.is_draft %}

View File

@ -32,7 +32,7 @@
<input class="d-none" id="emailPhotos" type="file" accept="image/*" />
</div>
<div class="d-flex">
<a href="{% url 'estimate_detail' estimate.pk %}" class="btn btn-link text-body fs-10 text-decoration-none">Discard</a>
<a href="{% url 'estimate_detail' request.dealer.slug estimate.pk %}" class="btn btn-link text-body fs-10 text-decoration-none">Discard</a>
<button class="btn btn-phoenix-primary fs-10" type="submit">Send<span class="fa-solid fa-paper-plane ms-1"></span></button>
</div>
</div>