From 417475ad54611f9c1db72eb7486e2dcba458c044 Mon Sep 17 00:00:00 2001 From: ismail Date: Wed, 24 Sep 2025 12:09:11 +0300 Subject: [PATCH] small fixes --- .../commands/set_custom_permissions.py | 20 +- inventory/models.py | 4 + inventory/views.py | 218 +++++++++--------- 3 files changed, 126 insertions(+), 116 deletions(-) diff --git a/inventory/management/commands/set_custom_permissions.py b/inventory/management/commands/set_custom_permissions.py index 91d7a577..c74db16f 100644 --- a/inventory/management/commands/set_custom_permissions.py +++ b/inventory/management/commands/set_custom_permissions.py @@ -7,16 +7,16 @@ from django_ledger.models import EstimateModel, BillModel, AccountModel, LedgerM class Command(BaseCommand): def handle(self, *args, **kwargs): - Permission.objects.get_or_create( - name="Can view crm", - codename="can_view_crm", - content_type=ContentType.objects.get_for_model(Lead), - ) - Permission.objects.get_or_create( - name="Can reassign lead", - codename="can_reassign_lead", - content_type=ContentType.objects.get_for_model(Lead), - ) + # Permission.objects.get_or_create( + # name="Can view crm", + # codename="can_view_crm", + # content_type=ContentType.objects.get_for_model(Lead), + # ) + # Permission.objects.get_or_create( + # name="Can reassign lead", + # codename="can_reassign_lead", + # content_type=ContentType.objects.get_for_model(Lead), + # ) Permission.objects.get_or_create( name="Can view sales", codename="can_view_sales", diff --git a/inventory/models.py b/inventory/models.py index 71fc8dfa..b2b2e1d8 100644 --- a/inventory/models.py +++ b/inventory/models.py @@ -2134,6 +2134,10 @@ class Lead(models.Model): slug = RandomCharField(length=8, unique=True) class Meta: + permissions = [ + ("can_view_crm", _("Can view CRM")), + ("can_reassign_lead", _("Can reassign lead")), + ] verbose_name = _("Lead") verbose_name_plural = _("Leads") indexes = [ diff --git a/inventory/views.py b/inventory/views.py index 27c904b9..cc435bfa 100644 --- a/inventory/views.py +++ b/inventory/views.py @@ -10259,31 +10259,52 @@ def submit_plan(request, dealer_slug): # @login_required def payment_callback(request, dealer_slug): + from django.db import transaction + payment_id = request.GET.get("id") payment_status = request.GET.get("status") message = request.GET.get("message", "") + if not payment_id: + logger.error("Missing payment ID in callback") + return render(request, "payment_failed.html", {"message": "Invalid request"}) + 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"} + with transaction.atomic(): + history = ( + models.PaymentHistory.objects + .select_for_update() + .filter(transaction_id=payment_id) + .first() ) - if history.status == "paid": - logger.info("Payment already processed. Redirecting to home.") - return redirect("home") + 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 history.status == "processing": + logger.warning(f"Payment {payment_id} is already being processed. Skipping.") + return redirect("home") + + if history.status == "failed" and payment_status != "paid": + logger.warning(f"Payment {payment_id} already failed. Ignoring.") + return render(request, "payment_failed.html", {"message": message or "Payment failed"}) + + history.status = "processing" + history.save(update_fields=["status"]) if payment_status == "paid": - logger.info( - f"Payment successful for transaction ID {payment_id}. Creating order..." - ) + 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: @@ -10291,116 +10312,101 @@ def payment_callback(request, dealer_slug): 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={ - "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}.") - - # Create or update UserPlan - if not hasattr(order.user, "userplan"): - UserPlan.objects.create( - user=order.user, - plan=order.plan, - # expire=datetime.now().date() + timedelta(days=order.get_plan_pricing().pricing.period) - ) - 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}.") + history.save(update_fields=["status"]) + return render(request, "payment_failed.html", {"message": "Invalid payment data"}) try: - # Complete the order (this may generate invoice, etc.) - order.complete_order() - history.status = "paid" - history.order = order # Link payment to order - history.save() + dealer = get_object_or_404(models.Dealer, slug=dealer_slug) + pp = get_object_or_404(PlanPricing, pk=plan_pricing_id) + + with transaction.atomic(): + history.refresh_from_db() + if history.status == "paid": + logger.info("Payment was already completed by another request. Skipping.") + return redirect("home") + + order = Order.objects.create( + user=dealer.user, + plan=pp.plan, + pricing=pp.pricing, + amount=pp.price, + currency="SAR", + tax=15, + status=Order.STATUS.NEW, + ) + logger.info(f"Order {order.id} created for user {dealer.user}") + + 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 " ", + }, + ) + + # Create UserPlan if missing + if not hasattr(order.user, "userplan"): + UserPlan.objects.create( + user=order.user, + plan=order.plan, + ) + logger.info(f"Created new UserPlan for user {order.user} with plan {order.plan}.") + else: + logger.info(f"UserPlan already exists for user {order.user}.") + + order.complete_order() + + history.status = "paid" + history.order = order + history.save(update_fields=["status", "order"]) invoice = order.get_invoices().first() - - logger.info(f"Order {order.id} completed. Rendering success page.") + logger.info(f"Order {order.id} completed successfully.") return render( request, "payment_success.html", {"order": order, "invoice": invoice} ) except Exception as e: - logger.exception(f"Error completing order {order.id}: {e}") + logger.exception(f"Error processing paid payment {payment_id}: {e}") + # Mark as failed history.status = "failed" - history.save() - return render( - request, "payment_failed.html", {"message": "Plan activation error"} - ) + history.save(update_fields=["status"]) + return render(request, "payment_failed.html", {"message": "Payment processing error"}) finally: - # 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() - for staff in dealer.get_staff(): - if not staff.user.is_active: - staff.activate_account() + try: + if dealer := getattr(order.user, "dealer", None): + if not dealer.user.is_active: + dealer.user.is_active = True + dealer.user.save() + for staff in dealer.get_staff(): + if not staff.user.is_active: + staff.activate_account() + except Exception as ex: + logger.warning(f"Failed to activate dealer/staff: {ex}") elif payment_status == "failed": - logger.warning( - f"Payment failed for transaction ID {payment_id}. Message: {message}" - ) + logger.warning(f"Payment failed for transaction ID {payment_id}. Message: {message}") history.status = "failed" - history.save() + history.save(update_fields=["status"]) return render(request, "payment_failed.html", {"message": message}) - return render(request, "payment_failed.html", {"message": "Unknown payment status"}) - + else: + logger.warning(f"Unknown payment status: {payment_status}") + history.status = "failed" + history.save(update_fields=["status"]) + return render(request, "payment_failed.html", {"message": "Unknown payment status"}) # @login_required # @permission_required("inventory.change_dealer", raise_exception=True) @@ -11555,16 +11561,16 @@ def upload_cars(request, dealer_slug, pk=None): try: if item: # data = [x.strip() for x in item.item_model.name.split("||")] - make = models.CarMake.objects.get(pk=item.addition_info.get("make")) - model = models.CarModel.objects.get(pk=item.addition_info.get("model")) - trim = models.CarTrim.objects.get(pk=item.addition_info.get("trim")) - serie = models.CarSerie.objects.get(pk=item.addition_info.get("serie")) - year = item.addition_info.get("year") + make = models.CarMake.objects.get(pk=item.additional_info.get("make")) + model = models.CarModel.objects.get(pk=item.additional_info.get("model")) + trim = models.CarTrim.objects.get(pk=item.additional_info.get("trim")) + serie = models.CarSerie.objects.get(pk=item.additional_info.get("serie")) + year = item.additional_info.get("year") exterior = models.ExteriorColors.objects.get( - pk=item.addition_info.get("exterior") + pk=item.additional_info.get("exterior") ) interior = models.InteriorColors.objects.get( - pk=item.addition_info.get("interior") + pk=item.additional_info.get("interior") ) receiving_date = timezone.now() vendor_model = item.bill_model.vendor