Money review: Bazaar rejection no longer burns the purchase token, read-only order admin, pack grant from order snapshot
- Bazaar verify: a rejected token is not stored on the order or the failed transaction, so the same purchase can still pay the right order; a refunded order refuses a new token (409). - OrderAdmin: every field read-only, no add/delete; Transaction admin read-only. - grant_pack uses the order's snapshot when the plan was deleted, logs ERROR when nothing is granted. - Pay link: authority is stored only if the order amount did not change during PaymentRequest. - PlanAdmin accepts only IRT (the app always renders toman). - reconcile_pending_order logs unexpected exceptions. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -8,6 +8,13 @@ from .models import BazaarCredential, Order, Transaction
|
||||
class TransactionInline(admin.TabularInline):
|
||||
model = Transaction
|
||||
extra = 0
|
||||
can_delete = False
|
||||
|
||||
def has_add_permission(self, request, obj=None):
|
||||
return False
|
||||
|
||||
def has_change_permission(self, request, obj=None):
|
||||
return False
|
||||
|
||||
|
||||
@admin.register(Order)
|
||||
@@ -21,10 +28,23 @@ class OrderAdmin(admin.ModelAdmin):
|
||||
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']
|
||||
|
||||
# همهی ستونها فقطخواندنیاند، نه فقط وضعیت: اجازهی change برای دو action لازم است، و تا امروز
|
||||
# همان اجازه فرمِ سفارش را هم باز میکرد. کارمندی که فقط باید لغو/بازپرداخت کند، روی سفارشِ در
|
||||
# انتظارِ خودش amount را ۱۰۰۰ و question_credits را ۱۰۰٬۰۰۰ کرد و با ۱۰۰۰ تومان ۱۰۰٬۰۰۰ سوال گرفت
|
||||
# (بازتولید شد). سفارش سندِ مالی است: از پنل ساخته یا پاک هم نمیشود؛ پاک شدنِ سفارشِ پرداختشده
|
||||
# تراکنشهایش را میبرد و توکنِ خریدش برای سفارشِ دیگری دوباره پذیرفته میشد.
|
||||
def get_readonly_fields(self, request, obj=None):
|
||||
return [f.name for f in self.model._meta.fields]
|
||||
|
||||
def has_add_permission(self, request):
|
||||
return False
|
||||
|
||||
def has_delete_permission(self, request, obj=None):
|
||||
return False
|
||||
|
||||
@admin.action(description='لغوِ سفارشِ در انتظار', permissions=['change'])
|
||||
def cancel_pending(self, request, queryset):
|
||||
from .services import OrderStateError, cancel_pending_order
|
||||
@@ -69,9 +89,21 @@ class OrderAdmin(admin.ModelAdmin):
|
||||
|
||||
@admin.register(Transaction)
|
||||
class TransactionAdmin(admin.ModelAdmin):
|
||||
"""فقط برای دیدن؛ تراکنش را فقط تأییدِ درگاه و actionهای سفارش میسازند (قیدِ یکتای gateway_ref_id
|
||||
جلوی تکرارِ توکنِ خرید است و ویرایش یا حذفِ دستیِ ردیف آن را برمیداشت)."""
|
||||
|
||||
list_display = ('id', 'order', 'gateway', 'status', 'paid_at')
|
||||
list_filter = ('gateway', 'status')
|
||||
|
||||
def has_add_permission(self, request):
|
||||
return False
|
||||
|
||||
def has_change_permission(self, request, obj=None):
|
||||
return False
|
||||
|
||||
def has_delete_permission(self, request, obj=None):
|
||||
return False
|
||||
|
||||
|
||||
@admin.register(BazaarCredential)
|
||||
class BazaarCredentialAdmin(admin.ModelAdmin):
|
||||
|
||||
Reference in New Issue
Block a user