update and formatting

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

1
.gitignore vendored
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -251,7 +251,7 @@
{% endif %} {% endif %}
</div> </div>
</div> </div>
</div> </div>
{% endblock %} {% endblock %}

View File

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

View File

@ -25,7 +25,7 @@
{% endblock customCSS %} {% endblock customCSS %}
{% block content %} {% block content %}
<div class="container-fluid" id="projectSummary"> <div class="container-fluid" id="projectSummary">
<div class="row g-3 justify-content-between align-items-end mb-4"> <div class="row g-3 justify-content-between align-items-end mb-4">
<div class="col-12 col-sm-auto"> <div class="col-12 col-sm-auto">
<h2 class="text-body-emphasis fw-bold mb-0">{{ _("Inventory") }}</h2> <h2 class="text-body-emphasis fw-bold mb-0">{{ _("Inventory") }}</h2>
@ -305,7 +305,7 @@
</div> </div>
</div> </div>
</div> </div>
</div> </div>
{% endblock %} {% endblock %}
{% block customJS %} {% block customJS %}

View File

@ -4,7 +4,7 @@
{{ _("Showing") }} {{ page_obj.start_index }} {{ _("to") }} {{ page_obj.end_index }} {{ _("Showing") }} {{ page_obj.start_index }} {{ _("to") }} {{ page_obj.end_index }}
{{ _("of") }} {{ page_obj.paginator.count }} {{ _("results") }} {{ _("of") }} {{ page_obj.paginator.count }} {{ _("results") }}
</div> </div>
<nav aria-label="Page navigation"> <nav aria-label="Page navigation">
<ul class="pagination mb-0"> <ul class="pagination mb-0">
{# First Page Link #} {# First Page Link #}
{% if page_obj.has_previous %} {% if page_obj.has_previous %}
@ -86,5 +86,5 @@
</li> </li>
{% endif %} {% endif %}
</ul> </ul>
</nav> </nav>
</div> </div>

View File

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

View File

@ -204,7 +204,7 @@
<td class="align-middle text-end white-space-nowrap pe-0 action"> <td class="align-middle text-end white-space-nowrap pe-0 action">
<div class="btn-reveal-trigger position-static"> <div class="btn-reveal-trigger position-static">
<button class="btn btn-sm dropdown-toggle dropdown-caret-none transition-none btn-reveal fs-10" type="button" data-bs-toggle="dropdown" data-boundary="window" aria-haspopup="true" aria-expanded="false" data-bs-reference="parent"><span class="fas fa-ellipsis-h fs-10"></span></button> <button class="btn btn-sm dropdown-toggle dropdown-caret-none transition-none btn-reveal fs-10" type="button" data-bs-toggle="dropdown" data-boundary="window" aria-haspopup="true" aria-expanded="false" data-bs-reference="parent"><span class="fas fa-ellipsis-h fs-10"></span></button>
<div class="dropdown-menu dropdown-menu-end py-2"><a class="dropdown-item text-success-dark" href="{% url 'order_detail' tx.pk %}">{{ _("View Sales Order Detail") }}</a> <div class="dropdown-menu dropdown-menu-end py-2"><a class="dropdown-item text-success-dark" href="{% url 'order_detail' request.dealer.slug tx.pk %}">{{ _("View Sales Order Detail") }}</a>
</div> </div>
</div> </div>
</td> </td>