diff --git a/.gitignore b/.gitignore index e5884ed1..8e0bbe6f 100644 --- a/.gitignore +++ b/.gitignore @@ -43,6 +43,7 @@ Makefile .idea/**/dynamic.xml .idea/**/uiDesigner.xml .idea/**/dbnavigator.xml +**/migrations/ # Gradle .idea/**/gradle.xml diff --git a/inventory/context_processors.py b/inventory/context_processors.py index 7f749d6a..8b35b790 100644 --- a/inventory/context_processors.py +++ b/inventory/context_processors.py @@ -1,5 +1,7 @@ from django.conf import settings +from inventory.utils import get_user_type + def currency_context(request): """ diff --git a/inventory/management/commands/generate_vin.py b/inventory/management/commands/generate_vin.py index 36dc6132..f2af0af1 100644 --- a/inventory/management/commands/generate_vin.py +++ b/inventory/management/commands/generate_vin.py @@ -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" diff --git a/inventory/middleware.py b/inventory/middleware.py index 3ee697b8..a68653ce 100644 --- a/inventory/middleware.py +++ b/inventory/middleware.py @@ -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,19 @@ 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 diff --git a/inventory/mixins.py b/inventory/mixins.py index ae55f329..e3e8fe81 100644 --- a/inventory/mixins.py +++ b/inventory/mixins.py @@ -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,13 @@ 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) diff --git a/inventory/models.py b/inventory/models.py index a9484260..f043ee29 100644 --- a/inventory/models.py +++ b/inventory/models.py @@ -2482,16 +2482,22 @@ class SaleOrder(models.Model): item_data = self.invoice.get_itemtxs_data() if item_data: return item_data - return [] # Return an empty list if no invoice or no item data - + return [] # Return an empty list if no invoice or no item data + @property def cars(self): # 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 # item_model should be a ForeignKey to your CarModel within ItemTransactionModel - return [x.item_model.car for x in self.items if hasattr(x, 'item_model') and hasattr(x.item_model, 'car')] - return [] # Return an empty list if no items or no associated cars + 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 class CustomGroup(models.Model): diff --git a/inventory/templatetags/custom_filters.py b/inventory/templatetags/custom_filters.py index ecc41190..031f26de 100644 --- a/inventory/templatetags/custom_filters.py +++ b/inventory/templatetags/custom_filters.py @@ -1,4 +1,3 @@ - from typing import Union from random import randint from django import template @@ -442,6 +441,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 +459,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 +469,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"], @@ -490,15 +492,15 @@ def transactions_table( qs = object_type.get_transaction_queryset(annotated=True) transaction_model_qs = qs.annotate( debit_credit_sort_order=Case( - 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 - default=Value(2), # Fallback for any other tx_type, if applicable - output_field=IntegerField() + 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 + default=Value(2), # Fallback for any other tx_type, if applicable + output_field=IntegerField(), ) ).order_by( - '-timestamp', # Primary sort: chronological (oldest first) - '-debit_credit_sort_order', # Secondary sort: Debits (0) before Credits (1) - 'pk' # Optional: Tie-breaker for consistent order + "-timestamp", # Primary sort: chronological (oldest first) + "-debit_credit_sort_order", # Secondary sort: Debits (0) before Credits (1) + "pk", # Optional: Tie-breaker for consistent order ) elif isinstance(object_type, InvoiceModel): transaction_model_qs = object_type.get_transaction_queryset( diff --git a/inventory/urls.py b/inventory/urls.py index fb9a78ac..5b6f9adb 100644 --- a/inventory/urls.py +++ b/inventory/urls.py @@ -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("/", 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,56 @@ urlpatterns = [ name="vendor_delete", ), # Car URLs - path("cars/upload_cars/", views.upload_cars, name="upload_cars"), - path("cars//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("/cars/upload_cars/", views.upload_cars, name="upload_cars"), path( - "cars/inventory////", + "/cars//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( + "/cars/inventory////", 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//", views.CarDetailView.as_view(), name="car_detail"), - path("cars//history/", views.car_history, name="car_history"), - path("cars//update/", views.CarUpdateView.as_view(), name="car_update"), - path("cars//delete/", views.CarDeleteView.as_view(), name="car_delete"), path( - "cars//finance/create/", + "/cars/inventory/stats", + views.inventory_stats_view, + name="inventory_stats", + ), + path( + "/cars/inventory/list", + views.CarListView.as_view(), + name="car_list", + ), + path( + "/cars//", + views.CarDetailView.as_view(), + name="car_detail", + ), + path("cars//history/", views.car_history, name="car_history"), + path( + "/cars//update/", + views.CarUpdateView.as_view(), + name="car_update", + ), + path( + "/cars//delete/", + views.CarDeleteView.as_view(), + name="car_delete", + ), + path( + "/cars//finance/create/", views.CarFinanceCreateView.as_view(), name="car_finance_create", ), path( - "cars/finance//update/", + "/cars/finance//update/", views.CarFinanceUpdateView.as_view(), name="car_finance_update", ), @@ -307,12 +337,16 @@ urlpatterns = [ views.bulk_update_car_price, name="bulk_update_car_price", ), - path("ajax/", views.AjaxHandlerView.as_view(), name="ajax_handler"), path( - "cars//add-color/", views.CarColorCreate.as_view(), name="add_color" + "/ajax/", views.AjaxHandlerView.as_view(), name="ajax_handler" ), path( - "car/colors//update/", + "/cars//add-color/", + views.CarColorCreate.as_view(), + name="add_color", + ), + path( + "/car/colors//update/", views.CarColorsUpdateView.as_view(), name="car_colors_update", ), @@ -353,35 +387,39 @@ urlpatterns = [ ), path("cars/inventory/search/", views.SearchCodeView.as_view(), name="car_search"), # path('cars//colors//update/',views.CarColorUpdateView.as_view(),name='color_update'), - path("cars/reserve//", views.reserve_car_view, name="reserve_car"), path( - "reservations//", + "/cars/reserve//", + views.reserve_car_view, + name="reserve_car", + ), + path( + "/reservations//", views.manage_reservation, name="reservations", ), path( - "cars//add-custom-card/", + "/cars//add-custom-card/", views.CustomCardCreateView.as_view(), name="add_custom_card", ), path( - "cars//add-registration/", + "/cars//add-registration/", views.CarRegistrationCreateView.as_view(), name="add_registration", ), # sales list path( - "sales/list/", + "/sales/list/", views.sales_list_view, name="sales_list", ), path( - "sale_orders//", + "/sale_orders//", views.SaleOrderDetailView.as_view(), name="order_detail", ), path( - "inventory//list/", + "/inventory//list/", views.InventoryListView.as_view(), name="inventort_list", ), @@ -612,142 +650,176 @@ urlpatterns = [ ), # Bank Account path( - "bank_accounts/", views.BankAccountListView.as_view(), name="bank_account_list" + "/bank_accounts/", + views.BankAccountListView.as_view(), + name="bank_account_list", ), path( - "bank_accounts//", + "/bank_accounts//", views.BankAccountDetailView.as_view(), name="bank_account_detail", ), path( - "bank_accounts/create/", + "/bank_accounts/create/", views.BankAccountCreateView.as_view(), name="bank_account_create", ), path( - "bank_accounts//update/", + "/bank_accounts//update/", views.BankAccountUpdateView.as_view(), name="bank_account_update", ), path( - "bank_accounts//delete/", + "/bank_accounts//delete/", views.bank_account_delete, name="bank_account_delete", ), # Account - path("coa_accounts/", views.AccountListView.as_view(), name="account_list"), path( - "coa_accounts//", + "/coa_accounts/", + views.AccountListView.as_view(), + name="account_list", + ), + path( + "/coa_accounts//", views.AccountDetailView.as_view(), name="account_detail", ), path( - "coa_accounts/create/", views.AccountCreateView.as_view(), name="account_create" + "/coa_accounts/create/", + views.AccountCreateView.as_view(), + name="account_create", ), path( - "coa_accounts//update/", + "/coa_accounts//update/", views.AccountUpdateView.as_view(), name="account_update", ), - path("coa_accounts//delete/", views.account_delete, name="account_delete"), - # Estimate - path("sales/estimates/", views.EstimateListView.as_view(), name="estimate_list"), path( - "sales/estimates//", + "/coa_accounts//delete/", + views.account_delete, + name="account_delete", + ), + ################################################# + # Estimate + ################################################# + path( + "/sales/estimates/", + views.EstimateListView.as_view(), + name="estimate_list", + ), + path( + "/sales/estimates//", views.EstimateDetailView.as_view(), name="estimate_detail", ), - path("sales/estimates/create/", views.create_estimate, name="estimate_create"), path( - "sales/estimates/create//", + "/sales/estimates/create/", + views.create_estimate, + name="estimate_create", + ), + path( + "/sales/estimates/create//", views.create_estimate, name="estimate_create_from_opportunity", ), path( - "sales/estimates//estimate_mark_as/", + "/sales/estimates//estimate_mark_as/", views.estimate_mark_as, name="estimate_mark_as", ), path( - "sales/estimates//preview/", + "/sales/estimates//preview/", views.EstimatePreviewView.as_view(), name="estimate_preview", ), path( - "sales/estimates//payment_request/", + "/sales/estimates//payment_request/", views.PaymentRequest.as_view(), name="payment_request", ), path( - "sales/estimates//send_email", views.send_email_view, name="send_email" + "/sales/estimates//send_email", + views.send_email_view, + name="send_email", ), path( - "sales/estimates//sale_order/", + "/sales/estimates//sale_order/", views.create_sale_order, name="create_sale_order", ), path( - "sales/estimates//sale_order//details/", + "/sales/estimates//sale_order//details/", views.SaleOrderDetail.as_view(), name="sale_order_details", ), path( - "sales/estimates//sale_order/preview/", + "/sales/estimates//sale_order/preview/", views.preview_sale_order, name="preview_sale_order", ), + ############################################### # Invoice - path("sales/invoices/", views.InvoiceListView.as_view(), name="invoice_list"), + ############################################### path( - "sales/invoices//create/", views.invoice_create, name="invoice_create" + "/sales/invoices/", + views.InvoiceListView.as_view(), + name="invoice_list", ), path( - "sales/invoices//", + "/sales/invoices//create/", + views.invoice_create, + name="invoice_create", + ), + path( + "/sales/invoices//", views.InvoiceDetailView.as_view(), name="invoice_detail", ), path( - "sales/invoices//preview/", + "/sales/invoices//preview/", views.InvoicePreviewView.as_view(), name="invoice_preview", ), path( - "sales/invoices//invoice_mark_as/", + "/sales/invoices//invoice_mark_as/", views.invoice_mark_as, name="invoice_mark_as", ), path( - "sales/invoices//draft_invoice_update/", + "/sales/invoices//draft_invoice_update/", views.DraftInvoiceModelUpdateFormView.as_view(), name="draft_invoice_update", ), path( - "sales/invoices//approved_invoice_update/", + "/sales/invoices//approved_invoice_update/", views.ApprovedInvoiceModelUpdateFormView.as_view(), name="approved_invoice_update", ), path( - "sales/invoices//paid_invoice_update/", + "/sales/invoices//paid_invoice_update/", views.PaidInvoiceModelUpdateFormView.as_view(), name="paid_invoice_update", ), # path('sales/estimates//preview/', views.EstimatePreviewView.as_view(), name='estimate_preview'), # path('send_email/', views.send_email, name='send_email'), # Payment - path("sales/payments/", views.PaymentListView, name="payment_list"), path( - "sales/payments//create/", + "/sales/payments/", views.PaymentListView, name="payment_list" + ), + path( + "/sales/payments//create/", views.PaymentCreateView, name="payment_create", ), # path("sales/payments/create/", views.PaymentCreateView, name="payment_create"), path( - "sales/payments//payment_details/", + "/sales/payments//payment_details/", views.PaymentDetailView, name="payment_details", ), path( - "sales/payments//payment_mark_as_paid/", + "/sales/payments//payment_mark_as_paid/", views.payment_mark_as_paid, name="payment_mark_as_paid", ), @@ -787,85 +859,90 @@ urlpatterns = [ name="item_expense_update", ), # Bills - path("items/bills/", views.BillListView.as_view(), name="bill_list"), + path( + "/items/bills/", + views.BillListView.as_view(), + name="bill_list", + ), # path("items/bills/create/", views.BillModelCreateViewView.as_view(), name="bill_create"), path( - "items/bills//create/", + "/items/bills//create/", views.BillModelCreateView.as_view(), name="bill-create", ), path( - "items/bills//create/purchase-order//", + "/items/bills//create/purchase-order//", views.BillModelCreateView.as_view(for_purchase_order=True), name="bill-create-po", ), path( - "items/bills//create/estimate//", + "/items/bills//create/estimate//", views.BillModelCreateView.as_view(for_estimate=True), name="bill-create-estimate", ), path( - "items/bills//detail//", + "/items/bills//detail//", views.BillModelDetailViewView.as_view(), name="bill-detail", ), path( - "items/bills//update//", + "/items/bills//update//", views.BillModelUpdateViewView.as_view(), name="bill-update", ), path( - "items/bills//update//items/", + "/items/bills//update//items/", views.BillModelUpdateViewView.as_view(action_update_items=True), name="bill-update-items", ), ############################################################ path( - "items/bills//actions//mark-as-draft/", + "/items/bills//actions//mark-as-draft/", views.BillModelActionMarkAsDraftView.as_view(), name="bill-action-mark-as-draft", ), path( - "items/bills//actions//mark-as-review/", + "/items/bills//actions//mark-as-review/", views.BillModelActionMarkAsInReviewView.as_view(), name="bill-action-mark-as-review", ), path( - "items/bills//actions//mark-as-approved/", + "/items/bills//actions//mark-as-approved/", views.BillModelActionMarkAsApprovedView.as_view(), name="bill-action-mark-as-approved", ), path( - "items/bills//actions//mark-as-paid/", + "/items/bills//actions//mark-as-paid/", views.BillModelActionMarkAsPaidView.as_view(), name="bill-action-mark-as-paid", ), path( - "items/bills//actions//mark-as-void/", + "/items/bills//actions//mark-as-void/", views.BillModelActionVoidView.as_view(), name="bill-action-mark-as-void", ), path( - "items/bills//actions//mark-as-canceled/", + "/items/bills//actions//mark-as-canceled/", views.BillModelActionCanceledView.as_view(), name="bill-action-mark-as-canceled", ), path( - "items/bills//actions//lock-ledger/", + "/items/bills//actions//lock-ledger/", views.BillModelActionLockLedgerView.as_view(), name="bill-action-lock-ledger", ), path( - "items/bills//actions//unlock-ledger/", + "/items/bills//actions//unlock-ledger/", views.BillModelActionUnlockLedgerView.as_view(), name="bill-action-unlock-ledger", ), path( - "items/bills//actions//force-migration/", + "/items/bills//actions//force-migration/", views.BillModelActionForceMigrateView.as_view(), name="bill-action-force-migrate", ), # path("items/bills/create/", views.bill_create, name="bill_create"), + # >>>>>>>>>>>>>>>>>>>>>>... path( "items/bills//bill_detail/", views.BillDetailView.as_view(), @@ -1039,82 +1116,81 @@ urlpatterns = [ ######### # Purchase Order path( - "purchase_orders/", + "/purchase_orders/", views.PurchaseOrderListView.as_view(), name="purchase_order_list", ), path( - "purchase_orders/new/", + "/purchase_orders/new/", views.PurchaseOrderCreateView, name="purchase_order_create", ), path( - "purchase_orders//detail/", + "/purchase_orders//detail/", views.PurchaseOrderDetailView.as_view(), name="purchase_order_detail", ), path( - "purchase_orders///update/", + "/purchase_orders///update/", views.PurchaseOrderUpdateView.as_view(), name="purchase_order_update", ), path( - "purchase_orders//update//update-items/", + "/purchase_orders//update//update-items/", views.PurchaseOrderUpdateView.as_view(action_update_items=True), name="purchase_order_update_items", ), path( - "purchase_orders/inventory_item/create/", + "/purchase_orders/inventory_item/create/", views.InventoryItemCreateView, name="inventory_item_create", ), path( - "purchase_orders/inventory_items_filter/", + "/purchase_orders/inventory_items_filter/", views.inventory_items_filter, name="inventory_items_filter", ), path( - "purchase_orders//delete//", + "/purchase_orders//delete//", views.PurchaseOrderModelDeleteView.as_view(), name="po-delete", ), path( - "purchase_orders///upload/", + "/purchase_orders///upload/", view=views.view_items_inventory, name="view_items_inventory", ), # Actions.... path( - "/action//mark-as-draft/", + "//action//mark-as-draft/", views.PurchaseOrderMarkAsDraftView.as_view(), name="po-action-mark-as-draft", ), path( - "/action//mark-as-review/", + "//action//mark-as-review/", views.PurchaseOrderMarkAsReviewView.as_view(), name="po-action-mark-as-review", ), path( - "/action//mark-as-approved/", + "//action//mark-as-approved/", views.PurchaseOrderMarkAsApprovedView.as_view(), name="po-action-mark-as-approved", ), path( - "/action//mark-as-fulfilled/", + "//action//mark-as-fulfilled/", views.PurchaseOrderMarkAsFulfilledView.as_view(), name="po-action-mark-as-fulfilled", ), path( - "/action//mark-as-canceled/", + "//action//mark-as-canceled/", views.PurchaseOrderMarkAsCanceledView.as_view(), name="po-action-mark-as-canceled", ), path( - "/action//mark-as-void/", + "//action//mark-as-void/", views.PurchaseOrderMarkAsVoidView.as_view(), name="po-action-mark-as-void", ), - ] handler404 = "inventory.views.custom_page_not_found_view" diff --git a/inventory/utils.py b/inventory/utils.py index 07b85a90..df409ca5 100644 --- a/inventory/utils.py +++ b/inventory/utils.py @@ -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"), diff --git a/inventory/views.py b/inventory/views.py index c31a1778..fe8643e4 100644 --- a/inventory/views.py +++ b/inventory/views.py @@ -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 @@ -352,6 +353,9 @@ class HomeView(LoginRequiredMixin, TemplateView): # 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,12 @@ 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 +636,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 +679,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 +992,13 @@ 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 +1037,13 @@ 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 +1156,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 +1172,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,11 +1258,9 @@ def inventory_stats_view(request): for make_data in inventory.values() ], } - print(result["makes"]) return render(request, "inventory/inventory_stats.html", {"inventory": result}) - # @login_required # def inventory_stats_view(request): # """ @@ -1362,7 +1377,6 @@ def inventory_stats_view(request): # return render(request, "inventory/inventory_stats.html", context) - class CarDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailView): """ Provides a detailed view of a single car instance. @@ -1427,7 +1441,10 @@ 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 +1491,13 @@ 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 +1894,13 @@ 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 +1942,17 @@ 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 +1980,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 +2009,9 @@ 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 +2019,9 @@ 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 +3483,26 @@ 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 +3522,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 +3613,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"] @@ -3592,6 +3636,11 @@ class AccountCreateView( 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 +3731,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"] @@ -3692,10 +3740,15 @@ class AccountUpdateView( 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,17 +3761,19 @@ 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 @login_required @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 sales, specific to the logged-in user's entity. Requires the user to have appropriate @@ -3732,7 +3787,7 @@ def sales_list_view(request): item transactions specific to the user's entity. :rtype: HttpResponse """ - dealer = get_user_type(request) + dealer = get_object_or_404(models.Dealer, slug=dealer_slug) entity = dealer.entity sale_orders = models.SaleOrder.objects.filter( @@ -3809,7 +3864,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 +3884,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 +4057,9 @@ 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 +4165,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 +4184,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 +4214,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 +4258,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 +4273,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 +4347,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 +4374,46 @@ 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 +4427,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 +4460,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 +4542,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 +4584,16 @@ 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 +4632,16 @@ 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 +4657,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 +4674,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 +4706,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 +4748,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 +4796,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 +4811,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 +4834,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 +4854,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 +4865,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 +4886,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 +4902,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 +4916,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 +4930,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 +4946,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 +4965,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 +4990,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 +6721,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 +6735,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 +6764,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 +6782,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 +6791,7 @@ class BillModelCreateView(CreateView): form_action = reverse( "bill-create", kwargs={ + "dealer_slug": self.kwargs["dealer_slug"], "entity_slug": self.kwargs["entity_slug"], }, ) @@ -6787,28 +6867,45 @@ 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 +6962,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 +6970,15 @@ 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 +7175,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 +7191,17 @@ 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 +7238,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 @@ -8325,7 +8428,7 @@ class JournalEntryListView(LoginRequiredMixin, ListView): model = JournalEntryModel context_object_name = "journal_entries" template_name = "ledger/journal_entry/journal_entry_list.html" - ordering=['-timestamp'] + ordering = ["-timestamp"] def get_queryset(self): qs = super().get_queryset() @@ -8437,12 +8540,14 @@ def JournalEntryTransactionsView(request, pk): # .order_by("account__code") # .all() # ) - qs=TransactionModel.objects.filter(journal_entry=journal).all() - transactions=qs.annotate( - debit_credit_sort_order=Case(When(tx_type='debit',then=Value(0)), - When(tx_type='credit',then=Value(1)), - output_field=IntegerField()) - ).order_by('debit_credit_sort_order') + qs = TransactionModel.objects.filter(journal_entry=journal).all() + transactions = qs.annotate( + debit_credit_sort_order=Case( + When(tx_type="debit", then=Value(0)), + When(tx_type="credit", then=Value(1)), + output_field=IntegerField(), + ) + ).order_by("debit_credit_sort_order") return render( request, "ledger/journal_entry/journal_entry_transactions.html", @@ -9041,15 +9146,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 +9162,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 +9203,10 @@ 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 +9216,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 +9238,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 +9284,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 +9308,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 +9333,39 @@ 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 +9384,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 +9413,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 +9444,24 @@ 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 +9474,6 @@ class BasePurchaseOrderActionActionView(BasePurchaseOrderActionActionViewBase): ) except ValidationError as e: print(e) - # messages.add_message( - # request, - # message=e.message, - # level=messages.ERROR, - # ) return response @@ -9405,7 +9481,14 @@ 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 +9517,14 @@ 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,14 +9572,14 @@ 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 + items_per_page = 30 paginator = Paginator(items, items_per_page) - page_number = request.GET.get('page') + page_number = request.GET.get("page") try: page_obj = paginator.get_page(page_number) except PageNotAnInteger: @@ -9501,21 +9589,26 @@ def view_items_inventory(request, entity_slug, po_pk): # If page is out of range (e.g. 9999), deliver last page of results. page_obj = paginator.get_page(paginator.num_pages) 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, 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": @@ -9525,7 +9618,9 @@ def upload_cars(request, pk=None): if item: item = ItemTransactionModel.objects.get(pk=pk) 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]) trim = models.CarTrim.objects.filter( name=data[3], id_car_serie__id_car_model=model.id_car_model @@ -9560,7 +9655,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") @@ -9663,5 +9758,3 @@ class InventoryListView(InventoryListViewBase): entity_slug=dealer.entity.slug, ) return super().get_queryset() - - diff --git a/templates/base.html b/templates/base.html index 76641d01..e8ead5bb 100644 --- a/templates/base.html +++ b/templates/base.html @@ -63,7 +63,7 @@ {% include 'header.html' %}
- + {% include "plans/expiration_messages.html" %} {% block period_navigation %} diff --git a/templates/bill/bill_detail.html b/templates/bill/bill_detail.html index aff81525..a0d3ecbe 100644 --- a/templates/bill/bill_detail.html +++ b/templates/bill/bill_detail.html @@ -42,11 +42,11 @@
- {% 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' %}
{% include 'bill/includes/card_vendor.html' with vendor=bill.vendor %}
- {% trans 'Bill List' %} @@ -65,7 +65,7 @@
{% trans 'Cash Account' %}: - {{ bill.cash_account.code }} @@ -80,7 +80,7 @@
{% trans 'Prepaid Account' %}: - {{ bill.prepaid_account.code }} @@ -94,7 +94,7 @@
{% trans 'Accounts Payable' %}: - {{ bill.unearned_account.code }} @@ -185,7 +185,7 @@ {% if bill_item.po_model_id %} + href="{% url 'purchase_order_detail' request.dealer.slug bill_item.po_model_id %}"> {% trans 'View PO' %} {% endif %} diff --git a/templates/bill/bill_update.html b/templates/bill/bill_update.html index 36b0d754..80417b15 100644 --- a/templates/bill/bill_update.html +++ b/templates/bill/bill_update.html @@ -17,9 +17,9 @@
- {% 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 %} -
+ {% csrf_token %}
@@ -30,12 +30,12 @@ {% trans 'Save Bill' %} - {% trans 'Back to Bill Detail' %} - {% trans 'Bill List' %} diff --git a/templates/bill/includes/card_bill.html b/templates/bill/includes/card_bill.html index 8695cbf3..38a0a563 100644 --- a/templates/bill/includes/card_bill.html +++ b/templates/bill/includes/card_bill.html @@ -201,48 +201,48 @@ - {% if page_obj.paginator.num_pages > 1 %} - -
- -
- {% include 'partials/pagination.html'%} -
- -
- {% endif %} - {% else %} - - {% trans "No Lead Yet" %} - - {% endif %}
-
-
{% endblock %} diff --git a/templates/crm/opportunities/opportunity_list.html b/templates/crm/opportunities/opportunity_list.html index bcdfb32a..13b82df5 100644 --- a/templates/crm/opportunities/opportunity_list.html +++ b/templates/crm/opportunities/opportunity_list.html @@ -90,14 +90,14 @@
{% include 'crm/opportunities/partials/opportunity_grid.html' %}
- {% if page_obj.paginator.num_pages > 1 %} - -
- -
- {% include 'partials/pagination.html'%} -
- -
- {% endif %} + {% if page_obj.paginator.num_pages > 1 %} + +
+ +
+ {% include 'partials/pagination.html'%} +
+ +
+ {% endif %} {% endblock %} \ No newline at end of file diff --git a/templates/customers/customer_list.html b/templates/customers/customer_list.html index a913ca67..cbf6a168 100644 --- a/templates/customers/customer_list.html +++ b/templates/customers/customer_list.html @@ -109,15 +109,15 @@ {% endif %} - {% if page_obj.paginator.num_pages > 1 %} - -
- -
- {% include 'partials/pagination.html'%} -
- -
- {% endif %} + {% if page_obj.paginator.num_pages > 1 %} + +
+ +
+ {% include 'partials/pagination.html'%} +
+ +
+ {% endif %} {% include 'modal/delete_modal.html' %} {% endblock %} \ No newline at end of file diff --git a/templates/dashboards/manager.html b/templates/dashboards/manager.html index 8abf93ee..16273b48 100644 --- a/templates/dashboards/manager.html +++ b/templates/dashboards/manager.html @@ -27,7 +27,7 @@
-

{{ invoices }}

+

{{ invoices }}

{{ _("Invoices")}}

diff --git a/templates/errors/400.html b/templates/errors/400.html index 9ec66330..c4b03af4 100644 --- a/templates/errors/400.html +++ b/templates/errors/400.html @@ -43,6 +43,6 @@

{% trans "400" %}

{% trans "Bad Request" %}

- {% trans "Go Back" %} + {% trans "Go Back" %}
{% endblock content %} \ No newline at end of file diff --git a/templates/groups/group_list.html b/templates/groups/group_list.html index 0ec7a11d..f50937a9 100644 --- a/templates/groups/group_list.html +++ b/templates/groups/group_list.html @@ -46,13 +46,13 @@
{% if page_obj.paginator.num_pages > 1 %} - -
- -
- {% include 'partials/pagination.html'%} -
- + +
+ +
+ {% include 'partials/pagination.html'%} +
+
{% endif %}
diff --git a/templates/header.html b/templates/header.html index 00227175..88723579 100644 --- a/templates/header.html +++ b/templates/header.html @@ -22,44 +22,44 @@
  • {% trans "Inventory"|capfirst %}
  • {% if perms.inventory.add_car %} {% endif %}
  • {% trans 'sales'|capfirst %}
  • {% if perms.django_ledger.add_estimatemodel %}
  • {% trans 'Financials' %}
  • {% if perms.django_ledger.view_accountmodel %} - {% endif %} + {% endif %}
    @@ -364,7 +364,7 @@ aria-label="Toggle Navigation"> - +
    haikal haikal diff --git a/templates/index.html b/templates/index.html index c11f442e..81918370 100644 --- a/templates/index.html +++ b/templates/index.html @@ -15,5 +15,4 @@
    {% endif %} - {% endblock %} \ No newline at end of file diff --git a/templates/inventory/add_custom_card.html b/templates/inventory/add_custom_card.html index 3d523782..e58b8ea8 100644 --- a/templates/inventory/add_custom_card.html +++ b/templates/inventory/add_custom_card.html @@ -2,7 +2,7 @@ {% load crispy_forms_filters %} + action="{% url 'add_custom_card' request.dealer.slug car.slug %}"> {% csrf_token %} {{ form|crispy }}
    diff --git a/templates/inventory/car_detail.html b/templates/inventory/car_detail.html index 194e36c1..8aeb3428 100644 --- a/templates/inventory/car_detail.html +++ b/templates/inventory/car_detail.html @@ -166,7 +166,11 @@ + 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' %} {% endif %} @@ -188,7 +192,12 @@ + data-bs-target="#mainModal" + hx-get="{% url 'add_registration' request.dealer.slug car.slug %}" + hx-target=".main-modal-body" + hx-swap="innerHTML" + >{% trans 'Add' %} + {% endif %} @@ -219,7 +228,7 @@
    {% if not car.get_transfer %} {% if perms.inventory.change_car %} - {% trans "Edit" %} @@ -277,7 +286,7 @@ {% if not car.get_transfer %} - {% trans "Edit" %} {% else %} {% trans "Cannot Edit, Car in Transfer." %} @@ -285,7 +294,7 @@ {% else %}

    {% trans "No finance details available." %}

    {% if perms.inventory.add_carfinance %} - {% trans "Add" %} {% endif %} @@ -327,7 +336,7 @@ {% if not car.get_transfer %} - {% trans "Edit" %} {% else %} {% trans "Cannot Edit, Car in Transfer." %} @@ -340,7 +349,7 @@

    {% trans "No color details available." %}

    {% if perms.inventory.add_carcolors %} - {{ _("Add Color") }} + {{ _("Add Color") }} {% endif %} @@ -372,7 +381,7 @@ {{ reservation.reserved_until }} {% if reservation.is_active %} - + {% csrf_token %}
    - +
    - {# Accept Invoice Button #} - {% if invoice.invoice_status == 'in_review' %} - - {% endif %} - - {# Record Payment Button #} - {% if invoice.invoice_status == 'approved' %} - - {# Icon always visible, adjusted margin #} - {% trans 'Record Payment' %} {# Text hidden on xs, shown on sm+ #} - - {% endif %} - - {# Mark as Paid Button #} - {% if not invoice.is_paid %} - - {% endif %} - - {# Preview Button #} - - {# Icon always visible, adjusted margin #} - {% trans 'Preview' %} {# Text hidden on xs, shown on sm+ #} - -
    + {% if invoice.invoice_status == 'in_review' %} + + {% endif %} + {% if invoice.invoice_status == 'approved' %} + {% trans 'Record Payment' %} + {% endif %} + {% if not invoice.is_paid %} + + {% endif %} + {% trans 'Preview' %} +
    {{invoice.amount_owned}} - +
    @@ -190,42 +171,42 @@
    - -
    -
    -
    {% trans "Invoice Number" %} :
    -

    {{invoice.invoice_number}}

    -
    -
    -
    {% trans "Invoice Date" %} :
    -

    {{invoice.created}}

    -
    -
    -
    {% trans "Customer Name" %} :
    -

    {{invoice.customer.customer_name}}

    -
    -
    -
    {% trans "Customer Email" %} :
    -

    {{invoice.customer.email}}

    -
    -
    -
    {% trans "Invoice Status" %} :
    -
    - {% if invoice.invoice_status == 'draft' %} - {% trans "Draft" %} - {% elif invoice.invoice_status == 'in_review' %} - {% trans "In Review" %} - {% elif invoice.invoice_status == 'approved' %} - {% trans "Approved" %} - {% elif invoice.invoice_status == 'declined' %} - {% trans "Declined" %} - {% elif invoice.invoice_status == 'paid' %} - {% trans "Paid" %} - {% endif %} -
    - -
    + +
    +
    +
    {% trans "Invoice Number" %} :
    +

    {{invoice.invoice_number}}

    +
    +
    +
    {% trans "Invoice Date" %} :
    +

    {{invoice.created}}

    +
    +
    +
    {% trans "Customer Name" %} :
    +

    {{invoice.customer.customer_name}}

    +
    +
    +
    {% trans "Customer Email" %} :
    +

    {{invoice.customer.email}}

    +
    +
    +
    {% trans "Invoice Status" %} :
    +
    + {% if invoice.invoice_status == 'draft' %} + {% trans "Draft" %} + {% elif invoice.invoice_status == 'in_review' %} + {% trans "In Review" %} + {% elif invoice.invoice_status == 'approved' %} + {% trans "Approved" %} + {% elif invoice.invoice_status == 'declined' %} + {% trans "Declined" %} + {% elif invoice.invoice_status == 'paid' %} + {% trans "Paid" %} + {% endif %}
    + +
    +
    diff --git a/templates/sales/invoices/invoice_list.html b/templates/sales/invoices/invoice_list.html index dd32f24d..73e937a9 100644 --- a/templates/sales/invoices/invoice_list.html +++ b/templates/sales/invoices/invoice_list.html @@ -4,7 +4,6 @@ {% block title %}{{ _("Invoices") }}{% endblock title %} {% block content %} -

    {% trans "Invoices" %}

    @@ -55,7 +54,7 @@ {{ invoice.created }} - {% trans "View" %} @@ -71,12 +70,12 @@
    - {% if page_obj.paginator.num_pages > 1 %} -
    -
    - {% include 'partials/pagination.html'%} -
    -
    + {% if page_obj.paginator.num_pages > 1 %} +
    +
    + {% include 'partials/pagination.html'%} +
    +
    {% endif %}
    {% endblock %} \ No newline at end of file diff --git a/templates/sales/journals/journal_list.html b/templates/sales/journals/journal_list.html index 00c7dbb5..c8e3bae1 100644 --- a/templates/sales/journals/journal_list.html +++ b/templates/sales/journals/journal_list.html @@ -29,7 +29,7 @@ {{ journal. }} {{ journal. }} - {% trans "view" %} @@ -44,11 +44,11 @@
    {% if page_obj.paginator.num_pages > 1%} -
    -
    - {% include 'partials/pagination.html'%} -
    -
    +
    +
    + {% include 'partials/pagination.html'%} +
    +
    {% endif %}
    {% endblock %} \ No newline at end of file diff --git a/templates/sales/orders/order_list.html b/templates/sales/orders/order_list.html index 8014aad8..e9c98181 100644 --- a/templates/sales/orders/order_list.html +++ b/templates/sales/orders/order_list.html @@ -53,11 +53,11 @@
    {% if page_obj.paginator.num_pages > 1%} -
    -
    - {% include 'partials/pagination.html'%} -
    -
    +
    +
    + {% include 'partials/pagination.html'%} +
    +
    {% endif %}
    diff --git a/templates/sales/payments/payment_form.html b/templates/sales/payments/payment_form.html index 78a6fac4..ca6966f5 100644 --- a/templates/sales/payments/payment_form.html +++ b/templates/sales/payments/payment_form.html @@ -23,7 +23,7 @@ {% endif %}
    {% if model %} -
    + {% endif %} {% csrf_token %} {{ form|crispy }} diff --git a/templates/sales/payments/payment_list.html b/templates/sales/payments/payment_list.html index b7ffa317..894411a4 100644 --- a/templates/sales/payments/payment_list.html +++ b/templates/sales/payments/payment_list.html @@ -30,11 +30,11 @@ {{ journal.je_number }} {% if journal.ledger.invoicemodel %} - {{ journal.ledger.invoicemodel }} + {{ journal.ledger.invoicemodel }} {% elif journal.ledger.billmodel %} - {{ journal.ledger.billmodel }} + {{ journal.ledger.billmodel }} {% else %} @@ -42,7 +42,7 @@ {{ journal.timestamp }} {{ journal.description }} - {% trans "View Tranactions"|capfirst %} + {% trans "View Tranactions"|capfirst %} {% empty %} @@ -54,11 +54,11 @@
    {% if page_obj.paginator.num_pages > 1 %} -
    -
    - {% include 'partials/pagination.html'%} -
    -
    +
    +
    + {% include 'partials/pagination.html'%} +
    +
    {% endif %}
    {% endblock %} \ No newline at end of file diff --git a/templates/sales/saleorder_detail.html b/templates/sales/saleorder_detail.html index a8fe773d..bfd46022 100644 --- a/templates/sales/saleorder_detail.html +++ b/templates/sales/saleorder_detail.html @@ -247,19 +247,19 @@
    - - -
    @@ -149,7 +149,7 @@ {% if tx.estimate %}

    - + {{tx.estimate.estimate_number}}
    {% if tx.estimate.status == "draft" %} @@ -167,7 +167,7 @@ {% if tx.invoice %}

    - + {{tx.invoice.invoice_number}}
    {% if tx.invoice.is_draft %} @@ -204,7 +204,7 @@

    - diff --git a/templates/send-mail.html b/templates/send-mail.html index 3acc1727..41a5dadc 100644 --- a/templates/send-mail.html +++ b/templates/send-mail.html @@ -32,7 +32,7 @@
    - Discard + Discard