from django.contrib import admin, messages from django.utils.html import format_html from . import bazaar from .models import BazaarCredential, Order, Transaction class TransactionInline(admin.TabularInline): model = Transaction extra = 0 @admin.register(Order) class OrderAdmin(admin.ModelAdmin): """ لغو و بازپرداخت فقط با دو action (از ۲۰۲۶-۱۰-۰۲)، نه با عوض کردنِ دستیِ وضعیت: «paid» را با دست «failed» کردن اعتبار یا اشتراکی را که سفارش داده بود پس نمی‌گرفت. برای همین وضعیت این‌جا فقط‌خواندنی است. منطق در payment.services (cancel_pending_order، refund_order) است و تست‌هایش در payment/tests_refund.py. """ list_display = ('id', 'user_id', 'plan_title', 'amount', 'currency', 'status', 'provider', 'created_at') list_filter = ('status', 'provider', 'currency') search_fields = ('=id', '=user_id') readonly_fields = ('status',) inlines = [TransactionInline] actions = ['cancel_pending', 'refund'] @admin.action(description='لغوِ سفارشِ در انتظار', permissions=['change']) def cancel_pending(self, request, queryset): from .services import OrderStateError, cancel_pending_order done = 0 for order in queryset: try: cancel_pending_order(order, staff=request.user.get_username()) done += 1 except OrderStateError as exc: self.message_user(request, str(exc), messages.WARNING) if done: self.message_user(request, f'{done} سفارش لغو شد.', messages.SUCCESS) @admin.action(description='بازپرداخت (پس گرفتنِ اعتبار/اشتراکِ سفارشِ پرداخت‌شده)', permissions=['change']) def refund(self, request, queryset): from django.template.response import TemplateResponse from .services import OrderStateError, refund_order if request.POST.get('confirm') != 'yes': return TemplateResponse(request, 'admin/payment/order/refund_confirm.html', { **self.admin_site.each_context(request), 'orders': list(queryset), 'opts': self.model._meta, 'title': 'تأییدِ بازپرداخت', }) for order in queryset: try: r = refund_order(order, staff=request.user.get_username()) except OrderStateError as exc: self.message_user(request, str(exc), messages.WARNING) continue if r['subscription_days']: what = f'{r["subscription_days"]} روز از اشتراک کم شد' else: what = f'{r["credits_taken"]} از {r["credits_granted"]} سوالِ بسته پس گرفته شد' if r['credits_taken'] < r['credits_granted']: what += f' (بقیه خرج شده بود)' if r['hearts_days']: what += f'، {r["hearts_days"]} روز قلبِ نامحدود کم شد' self.message_user(request, f'سفارشِ {order.id}: {what}. پول را از پنلِ درگاه برگردانید.', messages.SUCCESS) @admin.register(Transaction) class TransactionAdmin(admin.ModelAdmin): list_display = ('id', 'order', 'gateway', 'status', 'paid_at') list_filter = ('gateway', 'status') @admin.register(BazaarCredential) class BazaarCredentialAdmin(admin.ModelAdmin): """ اتصالِ یک‌باره به API بازار، بدونِ SSH: action لینکِ «اجازه» را می‌سازد؛ صاحبِ حسابِ بازار بازش می‌کند و بازار به verify برمی‌گردد. خودِ توکن هیچ‌جا نمایش داده نمی‌شود. """ list_display = ('__str__', 'authorized_at', 'pending_since') fields = ('connected', 'authorized_at', 'pending_since') readonly_fields = fields actions = ['make_authorize_link'] @admin.display(boolean=True, description='توکن دارد') def connected(self, obj): return bool(obj.refresh_token) def changelist_view(self, request, extra_context=None): BazaarCredential.objects.get_or_create(pk=1) return super().changelist_view(request, extra_context) def has_add_permission(self, request): return False @admin.action(description='ساختِ لینکِ اتصال به بازار', permissions=['change']) def make_authorize_link(self, request, queryset): url = bazaar.start_authorization() self.message_user(request, format_html( 'این لینک را صاحبِ حسابِ بازار باز کند و «اجازه» بزند: {0}', url, ), messages.SUCCESS)