Make every PaymentRequest changelist column sortable ascending/descending (computed amount/user/jalali columns get an `ordering` so they sort by the underlying DB field; only the "check success again" action button stays unsortable). Add a default `-created_at` ordering and surface `updated_at` as its own sortable column. The created_at/updated_at DateTimeRangeFilters are retained and compose with sorting. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
156 lines
6.2 KiB
Python
156 lines
6.2 KiB
Python
import logging
|
|
import io
|
|
|
|
import jdatetime
|
|
import xlsxwriter
|
|
from django.contrib import admin, messages
|
|
from django.db.models import Sum
|
|
from django.http import HttpResponse
|
|
from django.utils import timezone
|
|
from django.utils.html import format_html
|
|
from django.shortcuts import redirect, get_object_or_404
|
|
from django.urls import path
|
|
|
|
from .constans import StateChoices
|
|
from .forms import ApplicationGatewayPolicyForm
|
|
from .models import PaymentRequest, PNA, Fake, ApplicationGatewayPolicy, SEP
|
|
from rangefilter.filters import DateTimeRangeFilter
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class PNAAdmin(admin.ModelAdmin):
|
|
list_display = ['pna_status_code', 'pna_status_message', 'jajali_datetime', 'payment_request']
|
|
list_filter = ['pna_status']
|
|
search_fields = ['pna_status_code', 'pna_status_message']
|
|
|
|
|
|
class SEPAdmin(admin.ModelAdmin):
|
|
list_display = ['sep_status_code', 'sep_status_message', 'jajali_datetime', 'payment_request']
|
|
list_filter = ['sep_status']
|
|
search_fields = ['sep_status_code', 'sep_status_message']
|
|
|
|
|
|
@admin.action(description="export exel")
|
|
def export_exel(modeladmin, request, queryset):
|
|
output = io.BytesIO()
|
|
workbook = xlsxwriter.Workbook(output, options={"remove_timezone": True})
|
|
worksheet = workbook.add_worksheet()
|
|
|
|
for col, header in enumerate([]):
|
|
worksheet.write(0, col, header)
|
|
|
|
for row, payment_request in enumerate(queryset, 1):
|
|
worksheet.write(row, 0, payment_request.amount)
|
|
worksheet.write(row, 1, str(payment_request.pk))
|
|
worksheet.write(row, 2, str(payment_request.user.pk))
|
|
worksheet.write(row, 3, payment_request.jajali_datetime)
|
|
worksheet.write(row, 4, payment_request.gateway)
|
|
worksheet.write(row, 5, payment_request.get_state_display())
|
|
|
|
workbook.close()
|
|
|
|
# create a response
|
|
response = HttpResponse(content_type='application/vnd.ms-excel')
|
|
|
|
# tell the browser what the file is named
|
|
file_name = jdatetime.date.fromgregorian(date=timezone.now(), locale='fa_IR').strftime("%Y/%m/%d")
|
|
response['Content-Disposition'] = f'attachment;filename="{file_name}.xlsx"'
|
|
|
|
# put the spreadsheet data into the response
|
|
response.write(output.getvalue())
|
|
|
|
# return the response
|
|
return response
|
|
|
|
|
|
@admin.action(description="mark as registered")
|
|
def mark_as_registered(model_admin, request, queryset):
|
|
for obj in queryset:
|
|
obj.is_registered = True
|
|
obj.save(update_fields=['is_registered'])
|
|
|
|
|
|
class PaymentRequestAdmin(admin.ModelAdmin):
|
|
list_display = ['state', 'check_success_again', 'uuid', 'amount_display', 'user_display', 'jalali_datetime_display',
|
|
'is_registered', 'created_at', 'updated_at', 'application', 'gateway']
|
|
search_fields = ['application__uuid', 'application__client_id', 'amount', 'user__uuid']
|
|
list_filter = ['application', 'state', 'gateway', ('created_at', DateTimeRangeFilter), ('updated_at', DateTimeRangeFilter)]
|
|
# Every real column in list_display can be sorted ascending/descending by
|
|
# clicking its header; the computed columns declare an `ordering` so they
|
|
# sort by the underlying DB field. Only the "check success again" action
|
|
# button stays unsortable.
|
|
sortable_by = ['state', 'uuid', 'amount_display', 'user_display', 'jalali_datetime_display',
|
|
'is_registered', 'created_at', 'updated_at', 'application', 'gateway']
|
|
ordering = ['-created_at']
|
|
actions = [export_exel, mark_as_registered]
|
|
|
|
@admin.display(description='Splited amount', ordering='amount')
|
|
def amount_display(self, obj):
|
|
return obj.splited_amount
|
|
|
|
@admin.display(description='User', ordering='user')
|
|
def user_display(self, obj):
|
|
return obj.user_id
|
|
|
|
@admin.display(description='Jajali datetime', ordering='created_at')
|
|
def jalali_datetime_display(self, obj):
|
|
return obj.jajali_datetime
|
|
|
|
def changelist_view(self, request, extra_context=None):
|
|
extra_context = extra_context or {}
|
|
|
|
cl = self.get_changelist_instance(request)
|
|
queryset = cl.result_list
|
|
totals = queryset.aggregate(
|
|
total_amount=Sum('amount'),
|
|
)
|
|
extra_context['totals'] = totals
|
|
return super().changelist_view(request, extra_context=extra_context)
|
|
|
|
def check_success_again(self, obj):
|
|
if obj.state in [StateChoices.IPG_SUCCESS, StateChoices.WALLET_SUBMIT_PENDING,
|
|
StateChoices.WALLET_SUBMIT_SUCCESS]:
|
|
return format_html(
|
|
'<a class="button" href="{}">بررسی دوباره موفقیت</a>',
|
|
f'{obj.uuid}/check_success_again/'
|
|
)
|
|
return ''
|
|
|
|
def get_urls(self):
|
|
urls = super().get_urls()
|
|
custom_urls = [
|
|
path('<uuid:payment_request_uuid>/check_success_again/',
|
|
self.admin_site.admin_view(self.check_success_again_view))
|
|
]
|
|
return custom_urls + urls
|
|
|
|
def check_success_again_view(self, request, payment_request_uuid):
|
|
logger.info(f"[Admin panel] Check success again for payment_request_uuid={payment_request_uuid}")
|
|
payment_request: PaymentRequest = get_object_or_404(PaymentRequest, uuid=payment_request_uuid)
|
|
logger.info(f"[Admin panel] initial payment_request state={payment_request.state}")
|
|
if payment_request.state == StateChoices.IPG_SUCCESS:
|
|
payment_request.charge_wallet_balance()
|
|
elif payment_request.state == StateChoices.WALLET_SUBMIT_PENDING:
|
|
payment_request.deposit_submit()
|
|
elif payment_request.state == StateChoices.WALLET_SUBMIT_SUCCESS:
|
|
payment_request.deposit_verify()
|
|
payment_request.refresh_from_db()
|
|
logger.info(f"[Admin panel] final payment_request state={payment_request.state}")
|
|
self.message_user(request, f"درخواست {payment_request_uuid} بررسی شد ✅")
|
|
return redirect(request.META.get("HTTP_REFERER", ".."))
|
|
|
|
|
|
class FakeAdmin(admin.ModelAdmin):
|
|
list_display = ['created_at']
|
|
|
|
|
|
class ApplicationGatewayPolicyAdmin(admin.ModelAdmin):
|
|
form = ApplicationGatewayPolicyForm
|
|
|
|
|
|
admin.site.register(PaymentRequest, PaymentRequestAdmin)
|
|
admin.site.register(PNA, PNAAdmin)
|
|
admin.site.register(SEP, SEPAdmin)
|
|
admin.site.register(Fake, FakeAdmin)
|
|
admin.site.register(ApplicationGatewayPolicy, ApplicationGatewayPolicyAdmin)
|