From 7ff243d6dfb85dd7ff8d429d40f90c8f6b9af83e Mon Sep 17 00:00:00 2001 From: Faheedkhan Date: Thu, 3 Jul 2025 20:24:41 +0300 Subject: [PATCH 1/3] update --- inventory/utils.py | 30 ++++++++++++++++++++++++++++++ inventory/views.py | 1 + 2 files changed, 31 insertions(+) diff --git a/inventory/utils.py b/inventory/utils.py index 7f9f64e7..6a50c28d 100644 --- a/inventory/utils.py +++ b/inventory/utils.py @@ -29,6 +29,8 @@ from django.contrib.auth.models import User from django_q.tasks import async_task import secrets +import logging +logger=logging.getLogger(__name__) def make_random_password( length=10, allowed_chars="abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789" @@ -60,11 +62,19 @@ def get_jwt_token(): "api_token": settings.CAR_API_TOKEN, "api_secret": settings.CAR_API_SECRET, } + logger.debug(f"Attempting to fetch JWT token from: {url}") try: response = requests.post(url, headers=headers, json=data) response.raise_for_status() + #logging for success + logger.info("Successfully fetched JWT token.") return response.text except requests.exceptions.RequestException as e: + #logging for error + logger.error( + f"HTTP error fetching JWT token from {url}: ", + exc_info=True + ) print(f"Error obtaining JWT token: {e}") return None @@ -218,8 +228,22 @@ def reserve_car(car, request): ) car.status = models.CarStatusChoices.RESERVED car.save() + # --- Logging for Success --- + logger.info( + f"Car {car.id} ('{car.make} {car.model}') reserved successfully " + f"by user {request.user.id} ('{request.user.username}'). " + f"Reserved until: {reserved_until}." + ) + messages.success(request, _("Car reserved successfully.")) except Exception as e: + # --- Logging for Error --- + logger.error( + f"Error reserving car {car.id} ('{car.make} {car.model}') " + f"for user {request.user.id} ('{request.user.username}'). " + f"Error: {e}", + exc_info=True + ) messages.error(request, f"Error reserving car: {e}") return redirect("car_detail", dealer_slug=request.dealer.slug, slug=car.slug) @@ -1264,7 +1288,13 @@ def handle_account_process(invoice, amount, finance_data): ) try: car.item_model.for_inventory = False + logger.debug(f"Set item_model.for_inventory to False for car {car.vin}.") except Exception as e: + logger.error( + f"Error updating item_model.for_inventory for car {car.vin} (Invoice {invoice.invoice_number}): {e}", + exc_info=True + ) + print(e) car.finances.is_sold = True car.finances.save() diff --git a/inventory/views.py b/inventory/views.py index b4c1ff56..44dee31f 100644 --- a/inventory/views.py +++ b/inventory/views.py @@ -2860,6 +2860,7 @@ def GroupPermissionView(request, dealer_slug, pk): ("inventory", "notes"), ("inventory", "tasks"), ("inventory", "activity"), + ("django_ledger", "purchaseordermodel"), ("django_ledger", "bankaccountmodel"), -- 2.39.5 From 7a9b9901d5123c59c2cb3c5913092faf0c3fd71d Mon Sep 17 00:00:00 2001 From: Faheedkhan Date: Sun, 6 Jul 2025 12:47:20 +0300 Subject: [PATCH 2/3] update --- inventory/signals.py | 35 ++ inventory/views.py | 524 +++++++++++++++++- templates/bill/includes/card_bill.html | 9 +- templates/items/expenses/expenses_list.html | 4 + templates/items/service/service_list.html | 4 + .../bank_accounts/bank_account_list.html | 4 + templates/ledger/bills/bill_list.html | 4 + templates/vendors/vendors_list.html | 18 +- templates/vendors/view_vendor.html | 5 + 9 files changed, 589 insertions(+), 18 deletions(-) diff --git a/inventory/signals.py b/inventory/signals.py index c62421c3..652581d7 100644 --- a/inventory/signals.py +++ b/inventory/signals.py @@ -24,6 +24,10 @@ from django.utils.timezone import now from django.db import transaction from django_q.tasks import async_task +#logging +import logging +logger=logging.getLogger(__name__) + User = get_user_model() # @receiver(post_save, sender=models.SaleQuotation) @@ -88,7 +92,15 @@ def create_car_location(sender, instance, created, **kwargs): """ try: if created: + # Log that the signal was triggered for a new car + logger.debug(f"Post-save signal triggered for new Car (VIN: {instance.vin}). Attempting to create CarLocation.") + if instance.dealer is None: + # Log the critical data integrity error before raising + logger.error( + f"Attempted to create CarLocation for car (VIN: {instance.vin}) " + f"but 'dealer' field is missing. This indicates a data integrity issue or unhandled logic." + ) raise ValueError( f"Cannot create CarLocation for car {instance.vin}: dealer is missing." ) @@ -99,7 +111,18 @@ def create_car_location(sender, instance, created, **kwargs): showroom=instance.dealer, description=f"Initial location set for car {instance.vin}.", ) + # Log successful CarLocation creation + logger.info( + f"Successfully created CarLocation for new car (VIN: {instance.vin}) " + f"with owner '{instance.dealer.name}' (ID: {instance.dealer.pk})." + ) except Exception as e: + # --- Single-line log for general error during CarLocation creation --- + logger.error( + f"Failed to create CarLocation for car (VIN: {instance.vin}). " + f"An unexpected error occurred: {e}", + exc_info=True + ) print(f"Failed to create CarLocation for car {instance.vin}: {e}") @@ -517,6 +540,8 @@ def track_lead_status_change(sender, instance, **kwargs): :return: None """ if instance.pk: # Ensure the instance is being updated, not created + # Log that a lead update is being checked for status changes + logger.debug(f"Checking for status change on Lead ID: {instance.pk}.") try: old_lead = models.Lead.objects.get(pk=instance.pk) if old_lead.status != instance.status: # Check if status has changed @@ -526,7 +551,17 @@ def track_lead_status_change(sender, instance, **kwargs): new_status=instance.status, changed_by=instance.staff, # Assuming the assigned staff made the change ) + # --- Single-line log for successful status change and history creation --- + logger.info( + f"Lead ID: {instance.pk} status changed from '{old_lead.status}' to '{instance.status}'. " + f"LeadStatusHistory recorded by Staff: {instance.staff.username if instance.staff else 'N/A'}." + ) except models.Lead.DoesNotExist: + # --- Single-line log for expected Lead.DoesNotExist (e.g., during initial object creation) --- + logger.debug( + f"Lead ID: {instance.pk} not found in database when checking for status change. " + f"This might occur during initial object creation. Skipping status history tracking." + ) pass # Ignore if the lead doesn't exist (e.g., during initial creation) diff --git a/inventory/views.py b/inventory/views.py index 44dee31f..040bdef7 100644 --- a/inventory/views.py +++ b/inventory/views.py @@ -78,6 +78,9 @@ from django.views.generic import ( from django.db.models import Case, Value, IntegerField, When +#logger +logger=logging.getLogger(__name__) + # Django Ledger from django_ledger.io import roles from django_ledger.utils import accruable_net_summary @@ -320,12 +323,14 @@ def dealer_signup(request): if password != password_confirm: return JsonResponse({"error": _("Passwords do not match")}, status=400) try: - #TODO make this a django-q task + #Todo make this a django-q task create_user_dealer( email, password, name, arabic_name, phone, crn, vrn, address ) + logger.info(f"Delear created succesfully with emailID {email}") return JsonResponse({"message": _("User created successfully")}, status=200) except Exception as e: + logger.error(f"Error creating dealer with email id:{email} error:{e}") return JsonResponse({"error": str(e)}, status=400) return render( request, @@ -763,7 +768,10 @@ class AjaxHandlerView(LoginRequiredMixin, View): series = models.CarSerie.objects.filter(query).values( "id_car_serie", "name", "arabic_name", "generation_name" ) - except Exception: + logger.debug(f"Successfully fetched car series with query: {query}. Found {len(series)} results.") + + except Exception as e: + logger.error(f"Error fetching car series with query '{query}'. Details: {e}",exc_info=True ) return JsonResponse({"error": _("Server error occurred")}, status=500) return JsonResponse(list(series), safe=False) @@ -865,21 +873,40 @@ class SearchCodeView(LoginRequiredMixin, View): return JsonResponse({"success": False, "error": _("No image provided")}) try: + # --- Logging the start of image processing --- + logger.debug(f"Received image '{image_file.name}' ({image_file.size} bytes) for barcode/QR scan from user: {request.user.username}.") + np_arr = np.frombuffer(image_file.read(), np.uint8) image = cv2.imdecode(np_arr, cv2.IMREAD_COLOR) if image is None: + logger.error( + f"Invalid image format or corrupted file received for scan from user: {request.user.username}. " + f"Filename: {image_file.name}." + ) raise ValueError("Invalid image format") gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) decoded_objects = decode(gray) if not decoded_objects: + # --- Logging for no QR/Barcode detected --- + logger.info( + f"No QR/Barcode detected in image '{image_file.name}' " + f"from user: {request.user.username}." + ) return JsonResponse( {"success": False, "error": _("No QR/Barcode detected")} ) code = decoded_objects[0].data.decode("utf-8").strip() + # Use INFO or WARNING, as it's a common user-facing scenario, not necessarily a server error. + logger.info( + f"Scanned VIN '{code}' from image '{image_file.name}' not found in database for user: {request.user.username}." + ) car = get_object_or_404(models.Car, vin=code) + logger.info( + f"Successfully found car (VIN: {code}, ID: {car.id}) based on scanned code for user: {request.user.username}." + ) return JsonResponse( { "success": True, @@ -889,6 +916,11 @@ class SearchCodeView(LoginRequiredMixin, View): ) except Exception as e: + logger.error( + f"An unexpected error occurred during barcode/QR scan process for {image_file}" + f"from user: {request.user.username}. Error: {e}", + exc_info=True # CRUCIAL: Get the full traceback + ) return JsonResponse({"success": False, "error": str(e)}) @@ -1205,6 +1237,8 @@ def inventory_stats_view(request, dealer_slug): "trims": {}, } try: + logger.debug(f"Attempting to update inventory for Car ID: {car.id}, Make: {make.name} ({make.id_car_make}), " + f"Model: {model.name} ({model.id_car_model}).") inventory[make.id_car_make]["models"][model.id_car_model]["total_cars"] += 1 trim = car.id_car_trim @@ -1227,6 +1261,12 @@ def inventory_stats_view(request, dealer_slug): trim.id_car_trim ]["total_cars"] += 1 except Exception as e: + logger.error( + f"Error updating inventory counts for car ID {getattr(car, 'id', 'N/A')}. " + f"Make ID: {getattr(make, 'id_car_make', 'N/A')}, Model ID: {getattr(model, 'id_car_model', 'N/A')}, " + f"Trim ID: {getattr(trim, 'id_car_trim', 'N/A')}. Error: {e}", + exc_info=True + ) print(e) result = { "total_cars": total_cars, @@ -2331,10 +2371,16 @@ class CustomerCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView form.instance.dealer = dealer try: user = form.instance.create_user_model() + logger.info(f"Successfully created Customer with '{user.username}' (ID: {user.id}) " + f"with email '{user.email}' for dealer '{dealer.name}'.") except IntegrityError as e: if "UNIQUE constraint" in str(e): messages.error(self.request, _("Email already exists")) + logger.info(f"Attempted to create user with existing email '{form.instance.email}' " + f"for dealer '{dealer.name}'. Message: '{e}'") else: + logger.error(f"An unexpected IntegrityError occurred while creating user(customer) with email '{form.instance.email}' " + f"for dealer '{dealer.name}'. Error: {e}", exc_info=True ) messages.error(self.request, str(e)) return redirect("customer_create") customer = form.instance.create_customer_model() @@ -2699,15 +2745,36 @@ class GroupCreateView( dealer = get_object_or_404(models.Dealer, slug=self.kwargs["dealer_slug"]) instance = form.save(commit=False) group_name = f"{dealer.slug}_{instance.name}" + + logger.debug( + f"Attempting to create or get Django Group '{group_name}' " + f"for dealer '{dealer.name}' (ID: {dealer.id})." + ) + try: group, created = Group.objects.get_or_create(name=group_name) + if created: + logger.info(f"Successfully created new Django Group: '{group_name}'.") + else: + logger.info(f"Django Group '{group_name}' already exists and was retrieved.") instance.dealer = dealer instance.group = group instance.save() + logger.info( + f"Successfully created CustomGroup '{instance.name}' (ID: {instance.id}) " + f"linked to Django Group '{group.name}' for dealer '{dealer.name}'." + ) + except IntegrityError as e: from django.utils.translation import gettext_lazy as _ print(e) messages.error(self.request, _("Group name already exists")) + logger.error( + f"An unexpected IntegrityError occurred during CustomGroup creation " + f"for name '{instance.name}' (derived Django Group name '{group_name}') " + f"for dealer '{dealer.name}'. Error: {e}", + exc_info=True # CRUCIAL: Get the full traceback for unexpected errors + ) return redirect("group_create", dealer_slug=dealer.slug) if created: @@ -2881,6 +2948,10 @@ def GroupPermissionView(request, dealer_slug, pk): ] if request.method == "POST": + # Get user info for logging + user_id = request.user.id if request.user.is_authenticated else 'Anonymous' + user_username = request.user.username if request.user.is_authenticated else 'anonymous' + try: selected_ids = [int(id) for id in request.POST.getlist('permissions', [])] @@ -2906,11 +2977,23 @@ def GroupPermissionView(request, dealer_slug, pk): group.permissions.clear() if valid_perms.exists(): group.permissions.add(*valid_perms) - + + # --- Logging for successful permission update --- + logger.info( + f"Permissions for Group '{group.name}' (ID: {group.id}) " + f"successfully updated by user {user_username} (ID: {user_id}). " + f"Total permissions granted: {valid_perms.count()}." + ) messages.success(request, _("Permissions updated successfully")) return redirect("group_detail", dealer_slug=dealer_slug, pk=customgroup.pk) except Exception as e: + # --- Logging for error during permission update --- + logger.error( + f"Error updating permissions for Group '{group.name}' (ID: {group.id}) " + f"by user {user_username} (ID: {user_id}). Error: {e}", + exc_info=True # CRUCIAL: Includes the full traceback + ) messages.error(request, _("Error updating permissions: ") + str(e)) # GET request handling @@ -4558,7 +4641,10 @@ def create_sale_order(request, dealer_slug, pk): try: item.item_model.additional_info["car_info"]["status"] = "sold" item.item_model.save() + logger.debug(f"Car status updated to 'sold' for item.item_model PK: {getattr(item.item_model, 'pk', 'N/A')}.") + except KeyError: + logger.warning(f"KeyError: 'car_info' or 'status' key missing when attempting to update status to 'sold' for item.item_model PK: {getattr(item.item_model, 'pk', 'N/A')}.") pass item.item_model.car.mark_as_sold() @@ -4785,8 +4871,11 @@ def estimate_mark_as(request, dealer_slug, pk): ) car.status = "available" car.save() - except Exception: - pass + logger.debug(f"Car VIN '{car.vin}' status updated to 'available' for Estimate ID: {getattr(estimate, 'pk', 'N/A')}.") + except Exception as e: + logger.error(f"Failed to update car status to 'available' for Estimate ID: {getattr(estimate, 'pk', 'N/A')}. " + f"Attempted VIN: '{car.vin}'. Error: {e}",exc_info=True ) + messages.success(request, _("Quotation canceled successfully")) estimate.save() messages.success(request, _("Quotation marked as ") + mark.upper()) @@ -5206,6 +5295,11 @@ def PaymentCreateView(request, dealer_slug, pk): form = forms.PaymentForm() if request.method == "POST": form = forms.PaymentForm(request.POST) + + # --- Define user and model context for logging here + user_id = request.user.id if request.user.is_authenticated else 'Anonymous' + user_username = request.user.username if request.user.is_authenticated else 'anonymous' + if form.is_valid(): amount = form.cleaned_data.get("amount") invoice = form.cleaned_data.get("invoice") @@ -5226,11 +5320,20 @@ def PaymentCreateView(request, dealer_slug, pk): try: if invoice: set_invoice_payment(dealer, entity, invoice, amount, payment_method) + logger.info(f"User {user_username} (ID: {user_id}) successfully processed payment for Invoice ID: {invoice.pk} (Dealer: {dealer.slug}) Amount: {amount}, Method: {payment_method}.") elif bill: set_bill_payment(dealer, entity, bill, amount, payment_method) + logger.info(f"User {user_username} (ID: {user_id}) successfully processed payment for Bill ID: {bill.pk} (Dealer: {dealer.slug}) Amount: {amount}, Method: {payment_method}.") + messages.success(request, _("Payment created successfully")) return redirect(redirect_url, dealer_slug=dealer.slug, pk=model.pk) except Exception as e: + logger.error( + f"User {user_username} (ID: {user_id}) encountered error creating payment " + f"for (Dealer: {dealer.slug}). " + f"Attempted Amount: {amount}, Method: {payment_method}. Error: {e}", + exc_info=True + ) messages.error(request, f"Error creating payment: {str(e)}") else: messages.error(request, f"Invalid form data: {str(form.errors)}") @@ -5332,6 +5435,10 @@ def payment_mark_as_paid(request, dealer_slug, pk): dealer = get_object_or_404(models.Dealer, slug=dealer_slug) invoice = get_object_or_404(InvoiceModel, pk=pk) if request.method == "POST": + # Get user info for logging + user_id = request.user.id if request.user.is_authenticated else 'Anonymous' + user_username = request.user.username if request.user.is_authenticated else 'anonymous' + try: if invoice.amount_due == invoice.amount_paid: if not invoice.is_paid() and invoice.can_pay(): @@ -5346,13 +5453,28 @@ def payment_mark_as_paid(request, dealer_slug, pk): # invoice.ledger.post() invoice.ledger.save() + # --- Log successful operation --- + logger.info( + f"User {user_username} (ID: {user_id}) successfully marked Invoice ID: {invoice.pk} " + f"as paid and processed ledger entries for Dealer: {dealer_slug}." + ) messages.success(request, _("Payment created successfully")) else: + logger.warning( + f"User {user_username} (ID: {user_id}) attempted to mark Invoice ID: {invoice.pk} " + f"as paid, but it is not fully paid (Due: {invoice.amount_due}, Paid: {invoice.amount_paid}). " + f"Operation halted for Dealer: {dealer_slug}." + ) messages.error( request, _("Invoice is not fully paid, Payment cannot be marked as paid"), ) except Exception as e: + logger.error( + f"User {user_username} (ID: {user_id}) encountered an error while marking Invoice ID: {invoice.pk} " + f"as paid or processing ledger entries for Dealer: {dealer_slug}. Error: {e}", + exc_info=True + ) messages.error(request, f"Error: {str(e)}") return redirect("invoice_detail", dealer_slug=dealer_slug, pk=invoice.pk) @@ -5514,8 +5636,13 @@ def lead_create(request,dealer_slug): dealer = get_object_or_404(models.Dealer,slug=dealer_slug) if request.method == "POST": - form = forms.LeadForm(request.POST) + # get username for logging + user_username = request.user.username if request.user.is_authenticated else 'anonymous' + # Log intent before trying to mark as paid + + form = forms.LeadForm(request.POST) + # Filter car models based on the selected make (for POST requests) if "id_car_make" in request.POST: form.fields["id_car_model"].queryset = models.CarModel.objects.filter( @@ -5565,9 +5692,11 @@ def lead_create(request,dealer_slug): instance.organization = organization instance.next_action = LeadStatus.NEW instance.save() + logger.info(f"lead created successfully for dealer {dealer_slug} by user:{user_username}") messages.success(request, _("Lead created successfully")) return redirect("lead_list", dealer_slug=dealer.slug) else: + logger.error(f"error creating leading for dealer {dealer_slug} by user:{user_username}") messages.error( request, f"Lead was not created ... : {str(form.errors)}" ) @@ -5645,6 +5774,12 @@ def lead_tracking(request,dealer_slug): @login_required @permission_required("inventory.change_lead", raise_exception=True) def update_lead_actions(request,dealer_slug): + # get the user info for logging + user_username = request.user.username if request.user.is_authenticated else 'anonymous' + logger.debug( + f"User {user_username} is attempting to update lead actions " + f"for dealer '{dealer_slug}'. Request POST data: {request.POST.dict()}" + ) try: lead_id = request.POST.get("lead_id") current_action = request.POST.get("current_action") @@ -5652,6 +5787,11 @@ def update_lead_actions(request,dealer_slug): next_action_date = request.POST.get("next_action_date", None) if not all([lead_id, current_action, next_action]): + # Log for missing required fields + logger.warning( + f"User {user_username} submitted incomplete data to update lead actions " + f"for dealer '{dealer_slug}'. Missing fields: lead_id='{lead_id}', current_action='{current_action}', next_action='{next_action}'." + ) return JsonResponse( {"success": False, "message": "All fields are required"}, status=400 ) @@ -5664,6 +5804,12 @@ def update_lead_actions(request,dealer_slug): lead.status = current_action lead.next_action = next_action + # Log before updating lead fields + logger.debug( + f"User {user_username} found Lead ID: {lead.id} ('{lead.name}') " + f"for update. Current action: '{current_action}', Next action: '{next_action}', Next action date: '{next_action_date}'." + ) + # Parse the datetime string try: if next_action_date: @@ -5672,21 +5818,41 @@ def update_lead_actions(request,dealer_slug): next_action_date, "%Y-%m-%dT%H:%M" ) lead.next_action_date = timezone.make_aware(next_action_datetime) - except ValueError: + logger.debug(f"Lead ID: {lead.id} next_action_date parsed to {lead.next_action_date}.") + except ValueError as ve: + # Log for invalid date format + logger.warning( f"submitted invalid date format ('{next_action_date}') " + f"for Lead ID: {lead.id}. Error: {ve}" + ) return JsonResponse( {"success": False, "message": "Invalid date format"}, status=400 ) # Save the lead lead.save() - + # --- Logging for successful update (main try block success) --- + logger.info( + f"User {user_username} successfully updated Lead ID: {lead.id} ('{lead.name}'). " + f"New Status: '{lead.status}', Next Action: '{lead.next_action}', Next Action Date: '{lead.next_action_date}'." + ) return JsonResponse( {"success": True, "message": "Actions updated successfully"} ) except models.Lead.DoesNotExist: + # --- Logging for Lead not found --- + logger.warning( + f"User {user_username} attempted to update non-existent Lead with ID: '{lead_id}' " + f"for dealer '{dealer_slug}'. Returning 404." + ) return JsonResponse({"success": False, "message": "Lead not found"}, status=404) except Exception as e: + involved_lead_id = request.POST.get("lead_id", "N/A") + logger.error( + f"User {user_username} encountered an unexpected error while updating Lead ID: '{involved_lead_id}' " + f"for dealer '{dealer_slug}'. Error: {e}", + exc_info=True # CRUCIAL: Includes the full traceback + ) return JsonResponse({"success": False, "message": str(e)}, status=500) @@ -5747,13 +5913,43 @@ def LeadDeleteView(request,dealer_slug, slug): :param pk: The primary key identifier of the Lead to be deleted. :return: An HTTP redirect response to the lead list page. """ + # get the user info for logging + user_username = request.user.username if request.user.is_authenticated else 'anonymous' + lead = get_object_or_404(models.Lead, slug=slug) + + # Log intent before attempting deletion + logger.debug( + f"User {user_username} attempting to delete Lead ID: {lead.id} ('{lead.name}') " + f"and its associated customer/user for dealer '{dealer_slug}'." + ) + + try: User.objects.get(email=lead.customer.email).delete() lead.customer.delete() + # --- Single-line log for successful associated user/customer deletion --- + logger.info( + f"User {user_username} successfully deleted associated user and customer " + f"for Lead ID: {lead.id} ('{lead.name}') (Email: {lead.customer.email})." + ) except Exception as e: + # --- Single-line log for error during associated user/customer deletion --- + logger.error( + f"User {user_username} encountered an error deleting associated user/customer " + f"for Lead ID: {lead.id} ('{lead.name}') (Email: {getattr(lead.customer, 'email', 'N/A')}). " # Safely get email + f"Error: {e}", + exc_info=True + ) print(e) + lead_id_final = lead.id # Capture before deletion + lead_name_final = lead.name lead.delete() + # Log the final lead deletion, which happens unconditionally after the try-except + logger.info( + f"User {user_username} successfully deleted Lead ID: {lead_id_final} ('{lead_name_final}') " + f"for dealer '{dealer_slug}'." + ) messages.success(request, _("Lead deleted successfully")) return redirect("lead_list",dealer_slug=dealer_slug) @@ -5912,6 +6108,9 @@ def schedule_lead(request, dealer_slug,slug): dealer = get_object_or_404(models.Dealer, slug=dealer_slug) lead = get_object_or_404(models.Lead, slug=slug, dealer=dealer) if request.method == "POST": + # Get user info for logging (available throughout the POST handling) + user_username = request.user.username if request.user.is_authenticated else 'anonymous' + form = forms.ScheduleForm(request.POST) if form.is_valid(): instance = form.save(commit=False) @@ -5921,6 +6120,12 @@ def schedule_lead(request, dealer_slug,slug): instance.customer = lead.get_customer_model() service = Service.objects.get(name=instance.scheduled_type) + # Log attempt to create AppointmentRequest + logger.debug( + f"User {user_username} attempting to create AppointmentRequest " + f"for Lead ID: {lead.id} ('{lead.name}'). Service: '{service.name}', " + f"Scheduled At: '{instance.scheduled_at}', Duration: '{instance.duration}'." + ) try: appointment_request = AppointmentRequest.objects.create( @@ -5944,13 +6149,27 @@ def schedule_lead(request, dealer_slug,slug): ) instance.save() + # --- Logging for successful AppointmentRequest and Appointment creation --- + logger.info( + f"User {user_username} successfully scheduled Lead ID: {lead.id} ('{lead.name}'). " + f"AppointmentRequest ID: {appointment_request.pk}, Appointment ID: {appointment_request.appointment.pk}." + ) messages.success(request, _("Appointment Created Successfully")) try: if lead.opportunity: return redirect("opportunity_detail", dealer_slug=lead.dealer.slug, slug=lead.opportunity.slug) except models.Lead.opportunity.RelatedObjectDoesNotExist: + logger.info( + f"Lead ID: {lead.id} ('{lead.name}') has no associated opportunity. " + f"Redirecting to lead list for user {user_username} " + ) return redirect("lead_list", dealer_slug=lead.dealer.slug) else: + # Log for invalid form data + logger.warning( + f"User {user_username} submitted invalid schedule form data " + f"for Lead ID: {lead.id} ('{lead.name}'). Errors: {form.errors.as_json()}" + ) messages.error(request, f"Invalid form data: {str(form.errors)}") return redirect("schedule_lead", dealer_slug=dealer_slug,slug=lead.slug) form = forms.ScheduleForm() @@ -6012,6 +6231,9 @@ def send_lead_email(request,dealer_slug, slug, email_pk=None): dealer = get_object_or_404(models.Dealer, slug=dealer_slug) lead = get_object_or_404(models.Lead, slug=slug) status = request.GET.get("status") + # Get user info for logging + user_username = request.user.username if request.user.is_authenticated else 'anonymous' + if status == "draft": models.Email.objects.create( content_object=lead, @@ -6032,6 +6254,11 @@ def send_lead_email(request,dealer_slug, slug, email_pk=None): messages.success(request, _("Email Draft successfully")) try: if lead.opportunity: + # Log success when opportunity exists and redirecting + logger.info( + f"User {user_username} successfully drafted email for Lead ID: {lead.id} ('{lead.name}'). " + f"Lead has an Opportunity (ID: {lead.opportunity.pk}), redirecting to opportunity detail." + ) response = HttpResponse( redirect("opportunity_detail", dealer_slug=dealer_slug,slug=lead.opportunity.slug) ) @@ -6039,10 +6266,20 @@ def send_lead_email(request,dealer_slug, slug, email_pk=None): "opportunity_detail", args=[lead.opportunity.slug] ) else: + # Log success when no opportunity and redirecting to lead detail + logger.info( + f"User {user_username} successfully drafted email for Lead ID: {lead.id} ('{lead.name}'). " + f"Lead has no Opportunity, redirecting to lead detail." + ) response = HttpResponse(redirect("lead_detail", dealer_slug=dealer_slug,slug=lead.slug)) response["HX-Redirect"] = reverse("lead_detail", dealer_slug=dealer_slug,slug=lead.slug) return response except models.Lead.opportunity.RelatedObjectDoesNotExist: + # --- Log when Lead.opportunity does not exist (Draft status) --- + logger.info( + f"User {user_username} drafted email for Lead ID: {lead.id} ('{lead.name}'). " + f"Lead's opportunity does not exist. Redirecting to lead list." + ) return redirect("lead_list",dealer_slug=dealer.slug) if request.method == "POST": @@ -6077,8 +6314,18 @@ def send_lead_email(request,dealer_slug, slug, email_pk=None): messages.success(request, _("Email sent successfully")) try: if lead.opportunity: + # Log success when opportunity exists and redirecting after sending email + logger.info( + f"User {user_username} successfully sent email for Lead ID: {lead.id} ('{lead.name}'). " + f"Lead has an Opportunity (ID: {lead.opportunity.pk}), redirecting to opportunity detail." + ) return redirect("opportunity_detail", dealer_slug=dealer_slug,slug=lead.opportunity.slug) except models.Lead.opportunity.RelatedObjectDoesNotExist: + # --- Log when Lead.opportunity does not exist (POST request for sending) --- + logger.info( + f"User {user_username} sent email for Lead ID: {lead.id} ('{lead.name}'). " + f"Lead's opportunity does not exist. Redirecting to lead list." + ) return redirect("lead_list",dealer_slug=dealer_slug) msg = f""" السلام عليكم @@ -6790,8 +7037,12 @@ class BillModelCreateView(LoginRequiredMixin,PermissionRequiredMixin,CreateView) for_purchase_order = False for_estimate = False permission_required = "django_ledger.add_billmodel" - + + # Get user info for logging + def get(self, request, dealer_slug, **kwargs): + user_username = request.user.username if request.user.is_authenticated else 'anonymous' + dealer = get_object_or_404(models.Dealer, slug=dealer_slug) if not request.user.is_authenticated: return HttpResponseForbidden() @@ -6804,21 +7055,46 @@ class BillModelCreateView(LoginRequiredMixin,PermissionRequiredMixin,CreateView) estimate_qs, uuid__exact=self.kwargs["ce_pk"] ) if not estimate_model.can_bind(): + logger.warning( + f"User {user_username} attempted to create bill from Estimate ID: {estimate_model.uuid}, " + f"but estimate cannot be bound (already bound/invalid state). Returning 404." + ) return HttpResponseNotFound("404 Not Found") + logger.debug( + f"User {user_username} confirmed Estimate ID: {estimate_model.uuid} " + f"can be bound for bill creation." + ) return super(BillModelCreateView, self).get(request, dealer_slug, **kwargs) def get_context_data(self, **kwargs): context = super(BillModelCreateView, self).get_context_data(**kwargs) + user_username = self.request.user.username if self.request.user.is_authenticated else 'anonymous' + if self.for_purchase_order: po_pk = self.kwargs["po_pk"] po_item_uuids_qry_param = self.request.GET.get("item_uuids") if po_item_uuids_qry_param: try: po_item_uuids = po_item_uuids_qry_param.split(",") + # --- Single-line log for successful parsing --- + logger.debug( + f"User {user_username} successfully parsed item_uuids from query param " + f"for Purchase Order ID: {po_pk} " + ) except: + # --- Single-line log for error during parsing --- + logger.warning( + f"User {user_username}submitted invalid item_uuids query parameter " + f"'{po_item_uuids_qry_param}' for Purchase Order ID: {po_pk} " + f"Returning BadRequest." + ) return HttpResponseBadRequest() else: + logger.warning( + f"User {user_username} attempted to create bill from Purchase Order ID: {po_pk} " + f"Returning BadRequest." + ) return HttpResponseBadRequest() po_qs = PurchaseOrderModel.objects.for_entity( @@ -6903,9 +7179,13 @@ class BillModelCreateView(LoginRequiredMixin,PermissionRequiredMixin,CreateView) entity_slug=self.kwargs["entity_slug"], user_model=self.request.user ) po_model: PurchaseOrderModel = get_object_or_404(po_qs, uuid__exact=po_pk) - + + user_username = self.request.user.username if self.request.user.is_authenticated else 'anonymous' try: bill_model.can_bind_po(po_model, raise_exception=True) + logger.info( + f"User {user_username} successfully validated binding of Bill ID: {getattr(bill_model, 'pk', 'N/A')} " + f"to Purchase Order ID: {getattr(po_model, 'pk', 'N/A')}.") except ValidationError as e: messages.add_message( self.request, @@ -6913,6 +7193,10 @@ class BillModelCreateView(LoginRequiredMixin,PermissionRequiredMixin,CreateView) level=messages.ERROR, extra_tags="is-danger", ) + # --- Single-line log for ValidationError during binding validation --- + logger.warning(f"User {user_username} encountered a validation error " + f"while attempting to bind Bill ID: {getattr(bill_model, 'pk', 'N/A')} " + f"to Purchase Order ID: {getattr(po_model, 'pk', 'N/A')}. Error: {e.message}") return self.render_to_response(self.get_context_data(form=form)) po_model_items_qs = po_model.itemtransactionmodel_set.filter( @@ -8946,9 +9230,23 @@ def notifications_history(request): @login_required @permission_required("inventory.add_activity", raise_exception=True) def add_activity(request,dealer_slug, content_type, slug): + # Get user information for logging + user_username = request.user.username if request.user.is_authenticated else 'anonymous' + + + # Log the attempt to retrieve the model dynamically + logger.debug( + f"User {user_username} attempting to retrieve model " + f"for content_type '{content_type}' for dealer '{dealer_slug}'." + ) try: model = apps.get_model(f"inventory.{content_type}") except LookupError: + # --- Log for LookupError (Model not found) --- + logger.warning( + f"User {user_username} requested an invalid model content_type: '{content_type}'. " + f"Raising Http404." + ) raise Http404("Model not found") obj = get_object_or_404(model, slug=slug) @@ -8964,8 +9262,19 @@ def add_activity(request,dealer_slug, content_type, slug): activity.activity_type = form.cleaned_data["activity_type"] activity.save() + # --- Log for successful activity creation --- + logger.info( + f"User {user_username} successfully added activity (Type: {activity.activity_type}) " + f"for {content_type} ID: {obj.slug} ('{obj.name if hasattr(obj, 'name') else obj}') " + f"on dealer '{dealer_slug}'. Notes: '{activity.notes[:50]}...'." + ) messages.success(request, _("Activity added successfully")) else: + # --- Log for invalid form data --- + logger.warning( + f"User {user_username} submitted invalid activity form data " + f"for {content_type} ID: {obj.slug} (Dealer: {dealer_slug}). Errors: {form.errors.as_json()}" + ) messages.error(request, _("Activity form is not valid")) return redirect(f"{content_type}_detail",dealer_slug=dealer_slug, slug=slug) @@ -8973,9 +9282,21 @@ def add_activity(request,dealer_slug, content_type, slug): @login_required @permission_required("inventory.add_task", raise_exception=True) def add_task(request,dealer_slug, content_type, slug): + # Get user information for logging + user_username = request.user.username if request.user.is_authenticated else 'anonymous' + # Log the attempt to retrieve the model dynamically + logger.debug( + f"User {user_username} attempting to retrieve model " + f"for content_type '{content_type}' for dealer '{dealer_slug}'." + ) try: model = apps.get_model(f"inventory.{content_type}") except LookupError: + # --- Single-line log for LookupError (Model not found) --- + logger.warning( + f"User {user_username} requested an invalid model content_type: '{content_type}'. " + f"Raising Http404 for dealer '{dealer_slug}'." + ) raise Http404("Model not found") obj = get_object_or_404(model, slug=slug) @@ -8990,8 +9311,19 @@ def add_task(request,dealer_slug, content_type, slug): task.created_by = request.user task.due_date = form.cleaned_data["due_date"] task.save() + # --- Log for successful task creation --- + logger.info( + f"User {user_username} successfully added task " + f"(Assigned to: {task.assigned_to.username}) for {content_type} ID: {obj.slug} " + f"on dealer '{dealer_slug}'. Due: {task.due_date}, Notes: '{task.notes if hasattr(task, 'notes') else 'N/A'}'." + ) messages.success(request, _("Task added successfully")) else: + # --- Log for invalid form data --- + logger.warning( + f"User {user_username} submitted invalid task form data " + f"for {content_type} ID: {obj.slug} (Dealer: {dealer_slug}). Errors: {form.errors.as_json()}" + ) messages.error(request, _("Task form is not valid")) return redirect(f"{content_type}_detail",dealer_slug=dealer_slug, slug=slug) @@ -9013,9 +9345,21 @@ def update_task(request,dealer_slug, pk): @login_required @permission_required("inventory.add_note", raise_exception=True) def add_note(request,dealer_slug, content_type, slug): + # Get user information for logging + user_username = request.user.username if request.user.is_authenticated else 'anonymous' + # Log the attempt to retrieve the model dynamically + logger.debug( + f"User {user_username} attempting to retrieve model " + f"for content_type '{content_type}' for dealer '{dealer_slug}'." + ) try: model = apps.get_model(f"inventory.{content_type}") except LookupError: + # --- Single-line log for LookupError (Model not found) --- + logger.warning( + f"User {user_username} requested an invalid model content_type: '{content_type}'. " + f"Raising Http404 for dealer '{dealer_slug}'." + ) raise Http404("Model not found") dealer = get_object_or_404(models.Dealer,slug=dealer_slug) @@ -9029,8 +9373,19 @@ def add_note(request,dealer_slug, content_type, slug): note.created_by = request.user note.save() + # --- Single-line log for successful note creation --- + logger.info( + f"User {user_username} successfully added a note " + f"for {content_type} ID: {obj.slug} (Dealer: {dealer_slug}). " + f"Note: '{note.notes[:50]}...'." + ) messages.success(request, _("Note added successfully")) else: + # --- Single-line log for invalid form data --- + logger.warning( + f"User {user_username} submitted invalid note form data " + f"for {content_type} ID: {obj.slug} (Dealer: {dealer_slug}). Errors: {form.errors.as_json()}" + ) messages.error(request, _("Note form is not valid")) return redirect(f"{content_type}_detail",dealer_slug=dealer_slug, slug=slug) @@ -9217,14 +9572,33 @@ def AuditLogDashboardView(request,dealer_slug): @permission_required("inventory.change_dealer", raise_exception=True) def activate_account(request,dealer_slug, content_type, slug): get_object_or_404(models.Dealer,slug=dealer_slug) + + # Get user information for logging + user_username = request.user.username if request.user.is_authenticated else 'anonymous' + # Log the attempt to retrieve the model dynamically + logger.debug( + f"User {user_username}attempting to retrieve model " + f"for content_type '{content_type}' for dealer '{dealer_slug}' in activate_account view." + ) try: model = apps.get_model(f"inventory.{content_type}") except LookupError: + # --- Single-line log for LookupError (Model not found) --- + logger.warning( + f"User {user_username} requested an invalid model content_type: '{content_type}'. " + f"Raising Http404 in activate_account view for dealer '{dealer_slug}'." + ) raise Http404("Model not found") obj = get_object_or_404(model, slug=slug) if request.method == "POST": obj.activate_account() + # --- Single-line log for successful account activation --- + logger.info( + f"User {user_username} successfully activated account " + f"for {content_type} ID: {obj.slug} ('{obj.name if hasattr(obj, 'name') else obj}') " + f"on dealer '{dealer_slug}'." + ) messages.success(request, _("Account activated successfully")) return redirect("user_management", dealer_slug=dealer_slug) return render( @@ -9235,22 +9609,57 @@ def activate_account(request,dealer_slug, content_type, slug): @permission_required("inventory.change_dealer", raise_exception=True) def permenant_delete_account(request,dealer_slug, content_type, slug): get_object_or_404(models.Dealer,slug=dealer_slug) + # Get user information for logging + user_username = request.user.username if request.user.is_authenticated else 'anonymous' + logger.debug( + f"User {user_username} attempting to retrieve model " + f"for content_type '{content_type}' for permanent account deletion." + ) try: model = apps.get_model(f"inventory.{content_type}") except LookupError: + # Log if model not found + logger.warning( + f"User {user_username} requested an invalid model content_type: '{content_type}'. " + f"Raising Http404 for permanent account deletion (Dealer: {dealer_slug})." + ) raise Http404("Model not found") obj = get_object_or_404(model, slug=slug) if request.method == "POST": + logger.debug( + f"User {user_username} attempting to permanently delete account " + f"for {content_type} ID: {obj.slug} ('{obj.name if hasattr(obj, 'name') else obj}') " # Attempt to get name + f"on dealer '{dealer_slug}'." + ) try: obj.permenant_delete() + # Log successful permanent deletion + logger.info( + f"User {user_username} successfully permanently deleted account " + f"for {content_type} ID: {obj.slug} ('{obj.name if hasattr(obj, 'name') else obj}') " + f"on dealer '{dealer_slug}'." + ) messages.success(request, _("Account Deleted successfully")) except RestrictedError: + # Log restricted deletion + logger.warning( + f"User {user_username} attempted to permanently delete account " + f"for {content_type} ID: {obj.slug} ('{obj.name if hasattr(obj, 'name') else obj}') " + f"on dealer '{dealer_slug}', but deletion was restricted due to related objects." + ) messages.error( request, _("You cannot delete this account,it is related to another account"), ) except Exception as e: + # Log generic error during permanent deletion + logger.error( + f"User {user_username} encountered an unexpected error " + f"while permanently deleting account for {content_type} ID: {obj.slug} " + f"('{obj.name if hasattr(obj, 'name') else obj}') on dealer '{dealer_slug}'. Error: {e}", + exc_info=True + ) messages.error(request, _(f"Error deleting account: {e}")) return redirect("user_management", dealer_slug=dealer_slug) return render( @@ -9266,12 +9675,31 @@ def PurchaseOrderCreateView(request, dealer_slug,entity_slug): dealer = get_object_or_404(models.Dealer, slug=dealer_slug) entity = dealer.entity + # Get user information for logging + user_username = request.user.username if request.user.is_authenticated else 'anonymous' + if request.method == "POST": + # Log the attempt to create a purchase order + logger.debug( + f"User {user_username} attempting to create a Purchase Order " + f"for Entity ID: {entity.pk} ('{entity.name}'). Title: '{request.POST.get('po_title')}'." + ) try: po = entity.create_purchase_order(po_title=request.POST.get("po_title")) po.entity = entity po.save() + # --- Single-line log for successful purchase order creation --- + logger.info( + f"User {user_username} successfully created Purchase Order ID: {po.pk} " + f"('{po.po_title}') for Entity ID: {entity.pk} ('{entity.name}')." + ) except ValidationError as e: + # --- Single-line log for ValidationError --- + logger.warning( + f"User {user_username} encountered a validation error " + f"while creating a Purchase Order for Entity ID: {entity.pk} ('{entity.name}'). " + f"Error: {e}" + ) messages.error(request, str(e)) return redirect("purchase_order_create", dealer_slug=dealer.slug, entity_slug=entity.slug) messages.success(request, _("Purchase order created successfully")) @@ -9652,6 +10080,9 @@ class BasePurchaseOrderActionActionView(BasePurchaseOrderActionActionViewBase): def get(self, request, dealer_slug, entity_slug, po_pk, *args, **kwargs): # kwargs["user_model"] = self.request.user + # Get user information for logging + user_username = request.user.username if request.user.is_authenticated else 'anonymous' + dealer = get_object_or_404(models.Dealer, slug=dealer_slug) kwargs["user_model"] = dealer.entity.admin if not self.action_name: @@ -9660,15 +10091,31 @@ class BasePurchaseOrderActionActionView(BasePurchaseOrderActionActionViewBase): request, dealer_slug, entity_slug, po_pk, *args, **kwargs ) po_model: PurchaseOrderModel = self.get_object() - + + # Log the attempt to perform the action + logger.debug( + f"User {user_username} attempting to call action '{self.action_name}' " + f"on Purchase Order ID: {po_model.pk} (Entity: {entity_slug})." + ) try: getattr(po_model, self.action_name)(commit=self.commit, **kwargs) + # --- Single-line log for successful action --- + logger.info( + f"User {user_username} successfully executed action '{self.action_name}' " + f"on Purchase Order ID: {po_model.pk}." + ) messages.add_message( request, message="PO updated successfully.", level=messages.SUCCESS, ) except ValidationError as e: + # --- Single-line log for ValidationError --- + logger.warning( + f"User {user_username} encountered a validation error " + f"while performing action '{self.action_name}' on Purchase Order ID: {po_model.pk}. " + f"Error: {e}" + ) print(e) return response @@ -9784,7 +10231,15 @@ def upload_cars(request, dealer_slug, pk=None): po_item = None dealer = get_object_or_404(models.Dealer, slug=dealer_slug) response = redirect("upload_cars", dealer_slug=dealer_slug) + + # Get user information for logging + user_username = request.user.username if request.user.is_authenticated else 'anonymous' + if pk: + # Log retrieval of PO item for upload + logger.debug( + f"User {user_username} retrieved ItemTransactionModel PK: {pk} for car upload." + ) item = get_object_or_404(ItemTransactionModel, pk=pk) po_item = models.PoItemsUploaded.objects.get(dealer=dealer, item=item) response = redirect("upload_cars", dealer_slug=dealer_slug, pk=pk) @@ -9799,7 +10254,11 @@ def upload_cars(request, dealer_slug, pk=None): if request.method == "POST": csv_file = request.FILES.get("csv_file") - + # Log initial attempt to process data from item or POST + logger.debug( + f"User {user_username} starting car data processing for upload. " + f"Source: {'Existing Item PK: ' + str(pk) if pk else 'Form submission'}." + ) try: if item: data = [x.strip() for x in item.item_model.name.split("||")] @@ -9817,6 +10276,7 @@ def upload_cars(request, dealer_slug, pk=None): receiving_date = timezone.now() vendor_model = item.bill_model.vendor vendor = models.Vendor.objects.get(vendor_model=vendor_model) + logger.debug(f"User {user_username} extracted car details from existing Item PK: {pk}.") else: make = models.CarMake.objects.get(pk=request.POST.get("make")) model = models.CarModel.objects.get(pk=request.POST.get("model")) @@ -9833,12 +10293,23 @@ def upload_cars(request, dealer_slug, pk=None): request.POST.get("receiving_date"), "%Y-%m-%d" ) vendor = models.Vendor.objects.get(pk=request.POST.get("vendor")) + logger.debug(f"User {user_username} extracted car details from form submission.") except Exception as e: + # --- Log for errors during data extraction/retrieval --- + logger.error( + f"User {user_username} encountered an error while preparing car data " + f"for upload (Item PK: {pk if pk else 'N/A'}, Dealer: {dealer_slug}). Error: {e}", + exc_info=True + ) messages.error(request, f"Error processing CSV: {str(e)}") return response if not csv_file.name.endswith(".csv"): + logger.warning( + f"User {user_username} attempted to upload a non-CSV file " + f"('{csv_file.name}') for car upload. Dealer: {dealer_slug}." + ) messages.error(request, "Please upload a CSV file") return response try: @@ -9848,6 +10319,8 @@ def upload_cars(request, dealer_slug, pk=None): reader = csv.DictReader(csv_data) data = [x for x in reader] for row in data: + # Log VIN decoding and initial validation for each row + logger.debug(f"Processing VIN: {row.get('vin', 'N/A')} from CSV row by user {user_username}.") if result := decodevin(row["vin"]): if models.Car.objects.filter(vin=row["vin"]).exists(): messages.error(request, f"vin {row['vin']} already exists") @@ -9860,6 +10333,13 @@ def upload_cars(request, dealer_slug, pk=None): or (make.pk != car_make.pk) or (model.pk != car_model.pk) ): + logger.warning( + f"User {user_username} uploaded CSV with VIN '{row['vin']}' " + f"having data mismatch (Make/Model from VIN vs. expected). " + f"VIN Make: '{manufacturer_name}', Expected Make: '{make.name}'. " + f"VIN Model: '{model_name}', Expected Model: '{model.name}'. " + f"Returning error." + ) messages.error( request, f"invalid data at vin {row['vin']}, Please upload a valid CSV file", @@ -9881,10 +10361,21 @@ def upload_cars(request, dealer_slug, pk=None): ) car.add_colors(exterior=exterior, interior=interior) cars_created += 1 + logger.debug( + f"User {user_username} created Car ID: {car.pk} (VIN: {car.vin}). " + f"Count: {cars_created}." + ) if po_item: po_item.status = "uploaded" po_item.save() - + logger.info( + f"User {user_username} updated PoItemsUploaded status to 'uploaded' for Item PK: {item.pk}." + ) + # --- Log for successful CSV import and car creation --- + logger.info( + f"User {user_username} successfully imported {cars_created} cars " + f"for Item PK: {pk if pk else 'N/A'} (Dealer: {dealer_slug})." + ) messages.success(request, f"Successfully imported {cars_created} cars") return redirect( "view_items_inventory", @@ -9893,6 +10384,13 @@ def upload_cars(request, dealer_slug, pk=None): po_pk=item.po_model.pk,) except Exception as e: + # --- Log for general errors during CSV processing or car creation --- + logger.error( + f"User {user_username} encountered an unexpected error " + f"during CSV file processing or car creation for Item PK: {pk if pk else 'N/A'} " + f"(Dealer: {dealer_slug}). Error: {e}", + exc_info=True # Crucial for full traceback + ) messages.error(request, f"Error processing CSV: {str(e)}") return response diff --git a/templates/bill/includes/card_bill.html b/templates/bill/includes/card_bill.html index bf5ba80c..c50c4a24 100644 --- a/templates/bill/includes/card_bill.html +++ b/templates/bill/includes/card_bill.html @@ -63,10 +63,12 @@ class="btn btn-sm btn-phoenix-primary me-md-2"> {% trans 'View' %} + {% if perms.django_ledger.change_billmodel %} {% trans 'Update' %} + {% if bill.can_pay %} + {% endif %} {% empty %} diff --git a/templates/vendors/vendors_list.html b/templates/vendors/vendors_list.html index 10fe402c..a3408cf9 100644 --- a/templates/vendors/vendors_list.html +++ b/templates/vendors/vendors_list.html @@ -11,8 +11,11 @@

{{ _("Vendors") |capfirst }}

- {{ _("Add Vendor") }} + {% if perms.django_ledger.add_vendormodel %} + + {{ _("Add Vendor") }} + + {% endif %} {% include "partials/search_box.html" %} {% if page_obj.object_list %} @@ -121,14 +124,21 @@ diff --git a/templates/vendors/view_vendor.html b/templates/vendors/view_vendor.html index 669cb0e7..b8c84166 100644 --- a/templates/vendors/view_vendor.html +++ b/templates/vendors/view_vendor.html @@ -28,10 +28,14 @@ -- 2.39.5 From 85efc27bf14a33b993ef644d54aa0cb309a64fea Mon Sep 17 00:00:00 2001 From: Faheedkhan Date: Sun, 6 Jul 2025 12:54:14 +0300 Subject: [PATCH 3/3] update --- inventory/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inventory/views.py b/inventory/views.py index 8c7e40d5..641c7f9e 100644 --- a/inventory/views.py +++ b/inventory/views.py @@ -322,7 +322,7 @@ def dealer_signup(request): try: async_task(create_user_dealer( email, password, name, arabic_name, phone, crn, vrn, address - ) + )) logger.info(f"Delear created succesfully with emailID {email}") return JsonResponse({"message": _("User created successfully")}, status=200) except Exception as e: -- 2.39.5