From bae390746d7e18d2716f0144e851512852299845 Mon Sep 17 00:00:00 2001 From: ismail Date: Thu, 11 Sep 2025 20:58:40 +0300 Subject: [PATCH] merge --- car_inventory/asgi.py | 11 +- inventory/urls.py | 10 +- inventory/utils.py | 232 +++++++++++++------ inventory/views.py | 506 +++++++++++++++++++++++++++--------------- 4 files changed, 510 insertions(+), 249 deletions(-) diff --git a/car_inventory/asgi.py b/car_inventory/asgi.py index c1cd1ab8..398e69a3 100644 --- a/car_inventory/asgi.py +++ b/car_inventory/asgi.py @@ -25,6 +25,7 @@ from api import routing from inventory.notifications.sse import NotificationSSEApp from django.urls import re_path from django.core.asgi import get_asgi_application +from django.contrib.staticfiles.handlers import ASGIStaticFilesHandler # application = ProtocolTypeRouter( # { @@ -32,6 +33,9 @@ from django.core.asgi import get_asgi_application # # "websocket": AuthMiddlewareStack(URLRouter(routing.websocket_urlpatterns)), # } # ) + +app = get_asgi_application() + application = ProtocolTypeRouter( { "http": AuthMiddlewareStack( @@ -39,10 +43,13 @@ application = ProtocolTypeRouter( [ path("sse/notifications/", NotificationSSEApp()), re_path( - r"", get_asgi_application() + r"", app ), # All other routes go to Django ] ) ), } -) \ No newline at end of file +) + +if django.conf.settings.DEBUG: + application = ASGIStaticFilesHandler(app) \ No newline at end of file diff --git a/inventory/urls.py b/inventory/urls.py index d3b39132..db0b9639 100644 --- a/inventory/urls.py +++ b/inventory/urls.py @@ -1210,11 +1210,11 @@ urlpatterns = [ views.permenant_delete_account, name="permenant_delete_account", ), - path( - "/management/audit_log_dashboard/", - views.AuditLogDashboardView, - name="audit_log_dashboard", - ), + # path( + # "/management/audit_log_dashboard/", + # views.AuditLogDashboardView, + # name="audit_log_dashboard", + # ), ######### # Purchase Order path( diff --git a/inventory/utils.py b/inventory/utils.py index d4294400..b0673507 100644 --- a/inventory/utils.py +++ b/inventory/utils.py @@ -1604,17 +1604,17 @@ def _post_sale_and_cogs(invoice, dealer): # .first() # ) cash_acc = invoice.cash_account or dealer.settings.invoice_cash_account - + vat_acc = dealer.settings.invoice_tax_payable_account or entity.get_default_coa_accounts().filter(role_default=True, role=roles.LIABILITY_CL_TAXES_PAYABLE).first() - + car_rev = dealer.settings.invoice_vehicle_sale_account or entity.get_default_coa_accounts().filter(role_default=True, role=roles.INCOME_OPERATIONAL).first() - + add_rev = dealer.settings.invoice_additional_services_account - + cogs_acc = dealer.settings.invoice_cost_of_good_sold_account or entity.get_default_coa_accounts().filter(role_default=True, role=roles.COGS).first() - + inv_acc = dealer.settings.invoice_inventory_account or entity.get_default_coa_accounts().filter(role_default=True, role=roles.ASSET_CA_INVENTORY).first() - + net_car_price = Decimal(data["discounted_price"]) net_additionals_price = Decimal(data["additional_services"]["total"]) vat_amount = Decimal(data["vat_amount"]) @@ -1668,7 +1668,7 @@ def _post_sale_and_cogs(invoice, dealer): # tx_type='credit' # ) - + if car.get_additional_services_amount > 0: # Cr Sales – Additional Services if not add_rev: @@ -1885,23 +1885,22 @@ def create_make_accounts(dealer): ) -def handle_payment(request, order): +def handle_payment(request, dealer): url = "https://api.moyasar.com/v1/payments" callback_url = request.build_absolute_uri( - reverse("payment_callback", kwargs={"dealer_slug": request.dealer.slug}) + reverse("payment_callback", kwargs={"dealer_slug": dealer.slug}) ) - if request.user.is_authenticated: - email = request.POST["email"] - first_name = request.POST["first_name"] - last_name = request.POST["last_name"] - phone = request.POST["phone"] - - card_name = request.POST["card_name"] - card_number = str(request.POST["card_number"]).replace(" ", "").strip() - month = int(request.POST["card_expiry"].split("/")[0].strip()) - year = int(request.POST["card_expiry"].split("/")[1].strip()) - cvv = request.POST["card_cvv"] + email = request.POST.get("email") + first_name = request.POST.get("first_name") + last_name = request.POST.get("last_name") + phone = request.POST.get("phone") + card_name = request.POST.get("card_name") + card_number = str(request.POST.get("card_number", "")).replace(" ", "").strip() + expiry = request.POST.get("card_expiry", "").split("/") + month = int(expiry[0].strip()) if len(expiry) > 0 else 0 + year = int(expiry[1].strip()) if len(expiry) > 1 else 0 + cvv = request.POST.get("card_cvv") user_data = { "email": email, @@ -1909,65 +1908,166 @@ def handle_payment(request, order): "last_name": last_name, "phone": phone, } - try: - total = int((order.total() + order.tax * order.total() / 100) * 100) - except (AttributeError, TypeError): - raise ValueError("Order total or tax is invalid") - payload = json.dumps( - { - "amount": total, - "currency": "SAR", - "description": f"payment issued for {email}", - "callback_url": callback_url, - "source": { - "type": "creditcard", - "name": card_name, - "number": card_number, - "month": month, - "year": year, - "cvc": cvv, - "statement_descriptor": "Century Store", - "3ds": True, - "manual": False, - "save_card": False, - }, - "metadata": user_data, - } - ) + + # Get selected plan from session + selected_plan_id = request.session.get('pending_plan_id') + if not selected_plan_id: + raise ValueError("No pending plan found in session") + from plans.models import PlanPricing + + pp = PlanPricing.objects.get(pk=selected_plan_id) + # Calculate amount without creating order + amount_sar = pp.price # assuming price is in SAR + tax_amount = amount_sar * 15 / 100 + total = int((amount_sar + tax_amount) * 100) # convert to halalas + + # Pass plan & dealer info via metadata + metadata = { + **user_data, + "plan_pricing_id": selected_plan_id, + "dealer_slug": dealer.slug, + } + + payload = json.dumps({ + "amount": total, + "currency": "SAR", + "description": f"Payment for plan {pp.plan.name}", + "callback_url": callback_url, + "source": { + "type": "creditcard", + "name": card_name, + "number": card_number, + "month": month, + "year": year, + "cvc": cvv, + "statement_descriptor": "Century Store", + "3ds": True, + "manual": False, + "save_card": False, + }, + "metadata": metadata, + }) headers = {"Content-Type": "application/json", "Accept": "application/json"} auth = (settings.MOYASAR_SECRET_KEY, "") - response = requests.request("POST", url, auth=auth, headers=headers, data=payload) - if response.status_code == 400: - data = response.json() - if data["type"] == "validation_error": - errors = data.get("errors", {}) - if "source.year" in errors: - raise Exception("Invalid expiry year") - else: - raise Exception("Validation Error: ", errors) - else: - print("Failed to process payment:", data) - # + response = requests.post(url, auth=auth, headers=headers, data=payload) + if response.status_code != 201: + logger.error(f"Payment initiation failed: {response.text}") + return None, response.get("message") + data = response.json() - # order.status = AbstractOrder.STATUS.NEW - order.save() - # - amount = Decimal("{0:.2f}".format(Decimal(total) / Decimal(100))) - + # Create PaymentHistory WITHOUT linking to order (since order doesn't exist yet) + amount_decimal = Decimal("{0:.2f}".format(Decimal(total) / Decimal(100))) models.PaymentHistory.objects.create( user=request.user, - user_data=user_data, - amount=amount, + user_data=json.dumps(metadata), + amount=amount_decimal, currency=data["currency"], status=data["status"], transaction_id=data["id"], payment_date=data["created_at"], gateway_response=data, ) - transaction_url = data["source"]["transaction_url"] - return transaction_url + logger.info(f"Payment initiated: {data}") + return data["source"]["transaction_url"],None +# def handle_payment(request, order): +# logger.info(f"Handling payment for order {order}") +# url = "https://api.moyasar.com/v1/payments" +# callback_url = request.build_absolute_uri( +# reverse("payment_callback", kwargs={"dealer_slug": request.dealer.slug}) +# ) + +# logger.info(f"Got callback_url: {callback_url}") + +# if request.user.is_authenticated: +# email = request.POST["email"] +# first_name = request.POST["first_name"] +# last_name = request.POST["last_name"] +# phone = request.POST["phone"] + +# logger.info(f"Got user data: {email}, {first_name}, {last_name}, {phone}") + +# card_name = request.POST["card_name"] +# card_number = str(request.POST["card_number"]).replace(" ", "").strip() +# month = int(request.POST["card_expiry"].split("/")[0].strip()) +# year = int(request.POST["card_expiry"].split("/")[1].strip()) +# cvv = request.POST["card_cvv"] + +# logger.info(f"Got card data: {card_name}, {card_number[:4]}****, {month}, {year}, {cvv}") + +# user_data = { +# "email": email, +# "first_name": first_name, +# "last_name": last_name, +# "phone": phone, +# } +# try: +# total = int((order.total() + order.tax * order.total() / 100) * 100) +# except (AttributeError, TypeError): +# raise ValueError("Order total or tax is invalid") +# logger.info(f"Calculated total: {total}") + +# payload = json.dumps( +# { +# "amount": total, +# "currency": "SAR", +# "description": f"payment issued for {email}", +# "callback_url": callback_url, +# "source": { +# "type": "creditcard", +# "name": card_name, +# "number": card_number, +# "month": month, +# "year": year, +# "cvc": cvv, +# "statement_descriptor": "Century Store", +# "3ds": False, +# "manual": False, +# "save_card": False, +# }, +# "metadata": user_data, +# } +# ) + +# logger.info(f"Generated payload: {payload}") + +# headers = {"Content-Type": "application/json", "Accept": "application/json"} +# auth = (settings.MOYASAR_SECRET_KEY, "") +# response = requests.request("POST", url, auth=auth, headers=headers, data=payload) +# logger.info(f"Sent request to Moyasar API: {response.status_code}") + +# if response.status_code == 400: +# data = response.json() +# if data["type"] == "validation_error": +# errors = data.get("errors", {}) +# if "source.year" in errors: +# raise Exception("Invalid expiry year") +# else: +# raise Exception("Validation Error: ", errors) +# else: +# logger.error(f"Failed to process payment: {data}") +# # +# data = response.json() + +# # order.status = AbstractOrder.STATUS.NEW +# order.save() +# # +# amount = Decimal("{0:.2f}".format(Decimal(total) / Decimal(100))) + +# models.PaymentHistory.objects.create( +# user=request.user, +# user_data=user_data, +# amount=amount, +# currency=data["currency"], +# status=data["status"], +# transaction_id=data["id"], +# payment_date=data["created_at"], +# gateway_response=data, +# ) +# transaction_url = data["source"]["transaction_url"] +# logger.info(f"Created payment history and got transaction_url: {transaction_url}") +# return transaction_url # def get_user_quota(user): diff --git a/inventory/views.py b/inventory/views.py index 49d54ba6..2011d2a9 100644 --- a/inventory/views.py +++ b/inventory/views.py @@ -218,7 +218,7 @@ from .utils import ( from .tasks import create_accounts_for_make, create_user_dealer, send_email # djago easy audit log -from easyaudit.models import RequestEvent, CRUDEvent, LoginEvent +# from easyaudit.models import RequestEvent, CRUDEvent, LoginEvent logger = logging.getLogger(__name__) logging.basicConfig(level=logging.INFO) @@ -504,7 +504,8 @@ def general_dashboard(request,dealer_slug): total_vat_collected = total_vat_collected_from_cars + total_vat_collected_from_services total_revenue_generated = total_revenue_from_cars + total_revenue_from_services # total_expenses=sum([x.amount_paid for x in dealer.entity.get_bills().filter(bill_items__item_role="expense")]) - total_expenses=dealer.entity.get_bills().filter(bill_items__item_role="expense").aggregate(total=Sum('amount_paid'))['total'] or 0 + + total_expenses=dealer.entity.get_bills().filter(bill_items__item_role="expense").aggregate(total=Sum('amount_paid'))['total'] or 0 gross_profit = net_profit_from_cars+total_revenue_from_services - total_expenses # ---------------------------------------------------- @@ -9847,50 +9848,123 @@ def pricing_page(request, dealer_slug): -@login_required -@permission_required("inventory.change_dealer", raise_exception=True) -@require_POST +# @login_required +# @permission_required("inventory.change_dealer", raise_exception=True) +# @require_POST +# def submit_plan(request, dealer_slug): +# logger.info(f"dealer slug : {dealer_slug}") +# if request.method == "GET": +# logger.info("method is GET, redirecting to pricing page") +# return redirect("pricing_page", dealer_slug=dealer_slug) +# dealer = get_object_or_404(models.Dealer, slug=dealer_slug) +# logger.info(f"Selected dealer : {dealer}") +# selected_plan_id = request.POST.get("selected_plan") +# logger.info(f"Selected plan id : {selected_plan_id}") +# pp = PlanPricing.objects.get(pk=selected_plan_id) +# logger.info(f"Selected plan pricing : {pp}") +# order = None +# try: +# order = Order.objects.create( +# user=dealer.user, +# plan=pp.plan, +# pricing=pp.pricing, +# amount=pp.price, +# currency="SA", +# tax=15, +# status=1, +# ) +# logger.info(f"order created {order}") +# except Exception as e: +# logger.error(e) +# if not order: +# messages.error(request, _("Error creating order")) +# logger.error("unable to create order") +# return redirect("pricing_page", dealer_slug=dealer_slug) +# transaction_url = handle_payment(request, order) +# logger.info(f"Redirecting to : {transaction_url}") +# return redirect(transaction_url) def submit_plan(request, dealer_slug): + if request.method == "GET": + return redirect("pricing_page", dealer_slug=dealer_slug) + dealer = get_object_or_404(models.Dealer, slug=dealer_slug) selected_plan_id = request.POST.get("selected_plan") - pp = PlanPricing.objects.get(pk=selected_plan_id) - order = None - try: - order = Order.objects.create( - user=dealer.user, - plan=pp.plan, - pricing=pp.pricing, - amount=pp.price, - currency="SA", - tax=15, - status=1, - ) - logger.info(f"order created {order}") - except Exception as e: - logger.error(e) - if not order: - messages.error(request, _("Error creating order")) - logger.error("unable to create order") + if not selected_plan_id: + messages.error(request, _("No plan selected.")) return redirect("pricing_page", dealer_slug=dealer_slug) - transaction_url = handle_payment(request, order) + + # Store plan & dealer info in session for use in callback + request.session['pending_plan_id'] = selected_plan_id + request.session['pending_dealer_slug'] = dealer_slug + + # Initiate payment WITHOUT creating order + transaction_url,error = handle_payment(request, dealer) + if not transaction_url: + messages.error(request, _(f"Payment initiation failed. {error}")) + return redirect("pricing_page", dealer_slug=dealer_slug) + return redirect(transaction_url) -@login_required -@permission_required("inventory.change_dealer", raise_exception=True) +# @login_required def payment_callback(request, dealer_slug): - message = request.GET.get("message") - dealer = get_object_or_404(models.Dealer, slug=dealer_slug) payment_id = request.GET.get("id") - history = models.PaymentHistory.objects.filter(transaction_id=payment_id).first() payment_status = request.GET.get("status") - logger.info(f"Received payment callback for dealer_slug: {dealer_slug}, payment_id: {payment_id}, status: {payment_status}") - order = Order.objects.filter(user=dealer.user, status=1).first() # Status 1 = NEW - if history.status == "paid": - return redirect('home') - if payment_status == "paid": - logger.info(f"Payment successful for transaction ID {payment_id}. Processing order completion.") + message = request.GET.get("message", "") + logger.info(f"Received payment callback for dealer_slug: {dealer_slug}, payment_id: {payment_id}, status: {payment_status}") + + history = models.PaymentHistory.objects.filter(transaction_id=payment_id).first() + if not history: + logger.error(f"No PaymentHistory found for transaction_id: {payment_id}") + return render(request, "payment_failed.html", {"message": "Invalid transaction"}) + + if history.status == "paid": + logger.info("Payment already processed. Redirecting to home.") + return redirect('home') + + if payment_status == "paid": + logger.info(f"Payment successful for transaction ID {payment_id}. Creating order...") + + # Get metadata from PaymentHistory (passed during handle_payment) + metadata = history.user_data + if isinstance(metadata, str): + try: + metadata = json.loads(metadata) + except json.JSONDecodeError: + logger.error(f"Failed to decode metadata JSON: {metadata}") + metadata = {} + plan_pricing_id = metadata.get("plan_pricing_id") + dealer_slug_from_meta = metadata.get("dealer_slug") + + if not plan_pricing_id or dealer_slug_from_meta != dealer_slug: + logger.error("Invalid metadata in payment callback") + history.status = "failed" + history.save() + return render(request, "payment_failed.html", {"message": "Invalid payment data"}) + + dealer = get_object_or_404(models.Dealer, slug=dealer_slug) + pp = get_object_or_404(PlanPricing, pk=plan_pricing_id) + + # ✅ CREATE ORDER HERE + try: + order = Order.objects.create( + user=dealer.user, + plan=pp.plan, + pricing=pp.pricing, + amount=pp.price, + currency="SAR", # Fixed typo: was "SA" + tax=15, + status=Order.STATUS.NEW, # Use constant if available + ) + logger.info(f"Order {order.id} created for user {dealer.user}") + except Exception as e: + logger.exception(f"Failed to create order: {e}") + history.status = "failed" + history.save() + return render(request, "payment_failed.html", {"message": "Order creation failed"}) + + # Create or get BillingInfo billing_info, created = BillingInfo.objects.get_or_create( user=dealer.user, defaults={ @@ -9907,8 +9981,8 @@ def payment_callback(request, dealer_slug): else: logger.debug(f"Billing info already exists for user {dealer.user}.") + # Create or update UserPlan if not hasattr(order.user, 'userplan'): - print(order.get_plan_pricing().pricing.period) UserPlan.objects.create( user=order.user, plan=order.plan, @@ -9916,26 +9990,22 @@ def payment_callback(request, dealer_slug): ) logger.info(f"Created new UserPlan for user {order.user} with plan {order.plan}.") else: + # Optional: upgrade existing plan + # user_plan = order.user.userplan + # user_plan.plan = order.plan + # user_plan.save() logger.info(f"UserPlan already exists for user {order.user}.") try: - - # if order.user.userplan: - # user = order.user - # pricing = order.get_plan_pricing().pricing - - # logger.info(f"Processing order completion for {user} - upgrading to {order.plan}") - # user.userplan.plan = order.plan - # user.userplan.expire = datetime.now() + timedelta(days=pricing.period) - # user.userplan.save() - # user.save() - # logger.info(f"User {user} upgraded to {order.plan} plan successfully") - + # Complete the order (this may generate invoice, etc.) order.complete_order() history.status = "paid" + history.order = order # Link payment to order history.save() - logger.info(f"Order {order.id} for user {order.user} completed successfully. Payment history updated.") + invoice = order.get_invoices().first() + + logger.info(f"Order {order.id} completed. Rendering success page.") return render( request, "payment_success.html", @@ -9943,13 +10013,14 @@ def payment_callback(request, dealer_slug): ) except Exception as e: - logger.exception(f"Error completing order {order.id} for user {order.user}: {e}") - logger.error(f"Plan activation failed: {str(e)}") + logger.exception(f"Error completing order {order.id}: {e}") history.status = "failed" history.save() return render(request, "payment_failed.html", {"message": "Plan activation error"}) + finally: - if dealer := getattr(order.user,"dealer", None): + # Activate dealer & staff if needed + if dealer := getattr(order.user, "dealer", None): if not dealer.user.is_active: dealer.user.is_active = True dealer.user.save() @@ -9957,7 +10028,6 @@ def payment_callback(request, dealer_slug): if not staff.user.is_active: staff.activate_account() - logger.info(f"Order {order.id} for user {order.user} completed successfully. Payment history updated.") elif payment_status == "failed": logger.warning(f"Payment failed for transaction ID {payment_id}. Message: {message}") history.status = "failed" @@ -9965,6 +10035,90 @@ def payment_callback(request, dealer_slug): return render(request, "payment_failed.html", {"message": message}) return render(request, "payment_failed.html", {"message": "Unknown payment status"}) +# @login_required +# @permission_required("inventory.change_dealer", raise_exception=True) +# def payment_callback(request, dealer_slug): +# message = request.GET.get("message") +# dealer = get_object_or_404(models.Dealer, slug=dealer_slug) +# payment_id = request.GET.get("id") +# history = models.PaymentHistory.objects.filter(transaction_id=payment_id).first() +# payment_status = request.GET.get("status") +# logger.info(f"Received payment callback for dealer_slug: {dealer_slug}, payment_id: {payment_id}, status: {payment_status}") +# order = Order.objects.filter(user=dealer.user, status=1).first() # Status 1 = NEW +# logger.info(f"Retrieved order {order.id} for user {order.user}.") +# if history.status == "paid": +# logger.info(f"Payment history already marked as paid for transaction ID {payment_id}. Redirecting to home.") +# return redirect('home') +# if payment_status == "paid": +# logger.info(f"Payment successful for transaction ID {payment_id}. Processing order completion.") +# billing_info, created = BillingInfo.objects.get_or_create( +# user=dealer.user, +# defaults={ +# 'tax_number': dealer.vrn, +# 'name': dealer.arabic_name, +# 'street': dealer.address, +# 'zipcode': dealer.entity.zip_code or " ", +# 'city': dealer.entity.city or " ", +# 'country': dealer.entity.country or " ", +# } +# ) +# if created: +# logger.info(f"Created new billing info for user {dealer.user}.") +# else: +# logger.debug(f"Billing info already exists for user {dealer.user}.") + +# if not hasattr(order.user, 'userplan'): +# logger.info(f"Creating new UserPlan for user {order.user} with plan {order.plan}.") +# UserPlan.objects.create( +# user=order.user, +# plan=order.plan, +# # expire=datetime.now().date() + timedelta(days=order.get_plan_pricing().pricing.period) +# ) +# else: +# logger.info(f"UserPlan already exists for user {order.user}.") + +# try: +# logger.info(f"Processing order completion for {order.user} - upgrading to {order.plan}") +# order.complete_order() +# history.status = "paid" +# history.save() +# logger.info(f"Order {order.id} for user {order.user} completed successfully. Payment history updated.") +# invoice = order.get_invoices().first() +# logger.info(f"Redirecting to payment success page with invoice {invoice.id}.") +# return render( +# request, +# "payment_success.html", +# {"order": order, "invoice": invoice} +# ) + +# except Exception as e: +# logger.exception(f"Error completing order {order.id} for user {order.user}: {e}") +# logger.error(f"Plan activation failed: {str(e)}") +# history.status = "failed" +# history.save() +# logger.info(f"Redirecting to payment failed page with message {message}.") +# return render(request, "payment_failed.html", {"message": "Plan activation error"}) +# finally: +# if dealer := getattr(order.user,"dealer", None): +# logger.info(f"Activating dealer {dealer} and its staff.") +# if not dealer.user.is_active: +# logger.info(f"Activating dealer {dealer}.") +# dealer.user.is_active = True +# dealer.user.save() +# for staff in dealer.get_staff(): +# logger.info(f"Activating staff {staff}.") +# if not staff.user.is_active: +# staff.activate_account() +# logger.info(f"Order {order.id} for user {order.user} completed successfully. Payment history updated.") +# elif payment_status == "failed": +# logger.warning(f"Payment failed for transaction ID {payment_id}. Message: {message}") +# history.status = "failed" +# history.save() +# logger.info(f"Redirecting to payment failed page with message {message}.") +# return render(request, "payment_failed.html", {"message": message}) + +# logger.info(f"Redirecting to payment failed page with message {message}.") +# return render(request, "payment_failed.html", {"message": "Unknown payment status"}) # def payment_callback(request, dealer_slug): # message = request.GET.get("message") # dealer = get_object_or_404(models.Dealer, slug=dealer_slug) @@ -10308,140 +10462,140 @@ def user_management(request, dealer_slug): return render(request, "admin_management/user_management.html", context) -@login_required -@permission_required("inventory.change_dealer", raise_exception=True) -def AuditLogDashboardView(request, dealer_slug): - """ - Displays audit logs (User Actions, Login Events, Request Events) with pagination. - Log type is determined by the 'q' query parameter (e.g., ?q=userActions). - Pagination page number is passed as a query parameter (e.g., ?page=2). - """ - get_object_or_404(models.Dealer, slug=dealer_slug) - q = request.GET.get("q") # Get the log type from the 'q' query parameter - current_pagination_page = request.GET.get("page", 1) - context = {} - template_name = None - logs_per_page = 30 # Define logs per page once +# @login_required +# @permission_required("inventory.change_dealer", raise_exception=True) +# def AuditLogDashboardView(request, dealer_slug): +# """ +# Displays audit logs (User Actions, Login Events, Request Events) with pagination. +# Log type is determined by the 'q' query parameter (e.g., ?q=userActions). +# Pagination page number is passed as a query parameter (e.g., ?page=2). +# """ +# get_object_or_404(models.Dealer, slug=dealer_slug) +# q = request.GET.get("q") # Get the log type from the 'q' query parameter +# current_pagination_page = request.GET.get("page", 1) +# context = {} +# template_name = None +# logs_per_page = 30 # Define logs per page once - # --- Determine Data Source and Template based on 'q' parameter --- - if ( - q == "userRequests" - ): # This block handles cases where 'q' is 'requestEvents', None, or any other invalid value. - # It defaults to Request Logs if 'q' is not 'userActions' or 'loginEvents'. - template_name = "admin_management/request_logs.html" - context["title"] = "Request Logs Dashboard" - request_events = RequestEvent.objects.all().order_by("-datetime") - paginator = Paginator(request_events, logs_per_page) - try: - page_obj = paginator.page(current_pagination_page) - except PageNotAnInteger: - page_obj = paginator.page(1) - except EmptyPage: - page_obj = paginator.page(paginator.num_pages) +# # --- Determine Data Source and Template based on 'q' parameter --- +# if ( +# q == "userRequests" +# ): # This block handles cases where 'q' is 'requestEvents', None, or any other invalid value. +# # It defaults to Request Logs if 'q' is not 'userActions' or 'loginEvents'. +# template_name = "admin_management/request_logs.html" +# context["title"] = "Request Logs Dashboard" +# request_events = RequestEvent.objects.all().order_by("-datetime") +# paginator = Paginator(request_events, logs_per_page) +# try: +# page_obj = paginator.page(current_pagination_page) +# except PageNotAnInteger: +# page_obj = paginator.page(1) +# except EmptyPage: +# page_obj = paginator.page(paginator.num_pages) - elif q == "loginEvents": - template_name = "admin_management/auth_logs.html" - context["title"] = "Login Events Dashboard" - auth_events = LoginEvent.objects.all().order_by("-datetime") - paginator = Paginator(auth_events, logs_per_page) - try: - page_obj = paginator.page(current_pagination_page) - except PageNotAnInteger: - page_obj = paginator.page(1) - except EmptyPage: - page_obj = paginator.page(paginator.num_pages) +# elif q == "loginEvents": +# template_name = "admin_management/auth_logs.html" +# context["title"] = "Login Events Dashboard" +# auth_events = LoginEvent.objects.all().order_by("-datetime") +# paginator = Paginator(auth_events, logs_per_page) +# try: +# page_obj = paginator.page(current_pagination_page) +# except PageNotAnInteger: +# page_obj = paginator.page(1) +# except EmptyPage: +# page_obj = paginator.page(paginator.num_pages) - else: - template_name = "admin_management/model_logs.html" - context["title"] = "User Actions Dashboard" +# else: +# template_name = "admin_management/model_logs.html" +# context["title"] = "User Actions Dashboard" - # OPTIMIZATION: Get the QuerySet but don't evaluate it yet - model_events_queryset = CRUDEvent.objects.all().order_by("-datetime") +# # OPTIMIZATION: Get the QuerySet but don't evaluate it yet +# model_events_queryset = CRUDEvent.objects.all().order_by("-datetime") - # 1. Paginate the raw QuerySet FIRST - paginator = Paginator(model_events_queryset, logs_per_page) +# # 1. Paginate the raw QuerySet FIRST +# paginator = Paginator(model_events_queryset, logs_per_page) - try: - # Get the page object, which contains only the raw QuerySet objects for the current page - page_obj_raw = paginator.page(current_pagination_page) - except PageNotAnInteger: - page_obj_raw = paginator.page(1) - except EmptyPage: - page_obj_raw = paginator.page(paginator.num_pages) +# try: +# # Get the page object, which contains only the raw QuerySet objects for the current page +# page_obj_raw = paginator.page(current_pagination_page) +# except PageNotAnInteger: +# page_obj_raw = paginator.page(1) +# except EmptyPage: +# page_obj_raw = paginator.page(paginator.num_pages) - # 2. Now, process 'field_changes' ONLY for the events on the current page - processed_model_events_for_page = [] - for ( - event - ) in page_obj_raw.object_list: # Loop only through the current page's items - event_data = { - "datetime": event.datetime, - "user": event.user, - "event_type_display": event.get_event_type_display(), - "model_name": event.content_type.model, - "object_id": event.object_id, - "object_repr": event.object_repr, - "field_changes": [], - } +# # 2. Now, process 'field_changes' ONLY for the events on the current page +# processed_model_events_for_page = [] +# for ( +# event +# ) in page_obj_raw.object_list: # Loop only through the current page's items +# event_data = { +# "datetime": event.datetime, +# "user": event.user, +# "event_type_display": event.get_event_type_display(), +# "model_name": event.content_type.model, +# "object_id": event.object_id, +# "object_repr": event.object_repr, +# "field_changes": [], +# } - if event.changed_fields: - try: - changes = json.loads(event.changed_fields) - if isinstance(changes, dict): - for field_name, values in changes.items(): - old_value = ( - values[0] - if isinstance(values, list) and len(values) > 0 - else None - ) - new_value = ( - values[1] - if isinstance(values, list) and len(values) > 1 - else None - ) - event_data["field_changes"].append( - { - "field": field_name, - "old": old_value, - "new": new_value, - } - ) - elif changes is None: - event_data["field_changes"].append( - { - "field": "Info", - "old": "", - "new": "No specific field changes recorded (JSON was null)", - } - ) - else: # Handle valid JSON but not a dictionary (e.g., "[]", 123) - event_data["field_changes"].append( - { - "field": "Error", - "old": "", - "new": f"Unexpected JSON format: {type(changes).__name__}", - } - ) - except json.JSONDecodeError: - # Handle invalid JSON; you might log this error - event_data["field_changes"].append( - { - "field": "Error", - "old": "", - "new": "Invalid JSON in changed_fields", - } - ) - processed_model_events_for_page.append(event_data) +# if event.changed_fields: +# try: +# changes = json.loads(event.changed_fields) +# if isinstance(changes, dict): +# for field_name, values in changes.items(): +# old_value = ( +# values[0] +# if isinstance(values, list) and len(values) > 0 +# else None +# ) +# new_value = ( +# values[1] +# if isinstance(values, list) and len(values) > 1 +# else None +# ) +# event_data["field_changes"].append( +# { +# "field": field_name, +# "old": old_value, +# "new": new_value, +# } +# ) +# elif changes is None: +# event_data["field_changes"].append( +# { +# "field": "Info", +# "old": "", +# "new": "No specific field changes recorded (JSON was null)", +# } +# ) +# else: # Handle valid JSON but not a dictionary (e.g., "[]", 123) +# event_data["field_changes"].append( +# { +# "field": "Error", +# "old": "", +# "new": f"Unexpected JSON format: {type(changes).__name__}", +# } +# ) +# except json.JSONDecodeError: +# # Handle invalid JSON; you might log this error +# event_data["field_changes"].append( +# { +# "field": "Error", +# "old": "", +# "new": "Invalid JSON in changed_fields", +# } +# ) +# processed_model_events_for_page.append(event_data) - # 3. Replace the object_list of the original page_obj with the processed data - # This keeps all pagination properties (has_next, number, etc.) intact. - page_obj_raw.object_list = processed_model_events_for_page - page_obj = page_obj_raw # This will be passed to the context +# # 3. Replace the object_list of the original page_obj with the processed data +# # This keeps all pagination properties (has_next, number, etc.) intact. +# page_obj_raw.object_list = processed_model_events_for_page +# page_obj = page_obj_raw # This will be passed to the context - # Pass the final page object to the context - context["page_obj"] = page_obj +# # Pass the final page object to the context +# context["page_obj"] = page_obj - return render(request, template_name, context) +# return render(request, template_name, context) @login_required