This commit is contained in:
2026-09-04 13:05:54 +03:30
parent 463b812e3e
commit 17082a5ea9
43 changed files with 5663 additions and 285 deletions
+79 -4
View File
@@ -1,8 +1,13 @@
from django.contrib import admin, messages
from django.shortcuts import redirect
from django.db import transaction as db_transaction
from django.shortcuts import redirect, render
from django.urls import path, reverse
from django.utils.html import format_html
from apps.account.models import User
from .api.services import GemWalletService
from .forms import GemGiftForm
from .models import BazaarPurchase, BazaarSettings, FeatureActivation, FeaturePlan, FeaturePlanTier, GemPack, GemTransaction
from .providers.bazaar import BazaarAuthError, build_authorize_url, exchange_code_for_tokens
@@ -115,7 +120,9 @@ class FeaturePlanTierInline(admin.TabularInline):
@admin.register(FeaturePlan)
class FeaturePlanAdmin(admin.ModelAdmin):
list_display = ("name", "key", "is_purchasable")
list_display = ("name", "key", "is_free", "is_purchasable")
list_editable = ("is_free",)
list_filter = ("is_free", "is_purchasable")
search_fields = ("name", "key")
inlines = [FeaturePlanTierInline]
@@ -137,16 +144,84 @@ class FeatureActivationAdmin(admin.ModelAdmin):
@admin.register(GemTransaction)
class GemTransactionAdmin(admin.ModelAdmin):
"""تاریخچه‌ی جم — فقط‌خواندنی، به‌علاوه‌ی ابزارِ «هدیه‌ی جم».
هدیه از دکمه‌ی بالای همین صفحه انجام می‌شود، نه با ساختنِ دستیِ ردیفِ
تراکنش: اگر ادمین ردیف را دستی بسازد، User.gem_balance به‌روز نمی‌شود و
موجودی با تاریخچه واگرا می‌شود. مسیرِ درست از GemWalletService رد
می‌شود که هر دو را با هم و داخلِ یک تراکنش تغییر می‌دهد.
"""
list_display = ("user", "amount", "reason", "created_at")
list_filter = ("reason",)
search_fields = ("user__phone", "note")
readonly_fields = ("user", "amount", "reason", "note", "created_at")
change_list_template = "admin/billing/gemtransaction/change_list.html"
def has_add_permission(self, request):
# تراکنش جم فقط از سرویس‌های داخلی ساخته می‌شود، نه دستی از ادمین —
# برای اصلاح دستی از GemTransaction.Reason.ADMIN_ADJUSTMENT در شل/سرویس استفاده کن.
return False
def get_urls(self):
custom = [
path(
"gift/",
self.admin_site.admin_view(self.gift_view),
name="billing_gemtransaction_gift",
),
]
return custom + super().get_urls()
def gift_view(self, request):
if request.method == "POST":
form = GemGiftForm(request.POST)
if form.is_valid():
return self._apply_gift(request, form)
else:
form = GemGiftForm()
return render(
request,
"admin/billing/gem_gift.html",
{
**self.admin_site.each_context(request),
"title": "هدیه‌ی جم",
"form": form,
"opts": self.model._meta,
},
)
@db_transaction.atomic
def _apply_gift(self, request, form: GemGiftForm):
amount = form.cleaned_data["amount"]
note = form.cleaned_data["note"] or f"هدیه‌ی ادمین ({request.user})"
users, missing = form.existing_and_missing()
if missing and form.cleaned_data["create_missing"]:
created = [User.objects.create(phone=phone) for phone in missing]
users += created
missing = []
for user in users:
GemWalletService.add_gems(
user,
amount,
GemTransaction.Reason.ADMIN_ADJUSTMENT,
note=note,
)
if users:
messages.success(
request,
f"{amount} جم به {len(users)} شماره اضافه شد.",
)
if missing:
# هشدار و نه خطا: بقیه‌ی شماره‌ها شارژ شده‌اند و ادمین باید
# بداند دقیقا کدام‌ها جا مانده‌اند.
messages.warning(
request,
"این شماره‌ها کاربر ندارند و رد شدند: " + "، ".join(missing),
)
return redirect("admin:billing_gemtransaction_changelist")
@admin.register(BazaarPurchase)
class BazaarPurchaseAdmin(admin.ModelAdmin):