from django.contrib import admin from django.core.exceptions import PermissionDenied from django.db import connection from . import models, policies, services @admin.register(models.PaymentAccessLink) class PaymentAccessLinkAdmin(admin.ModelAdmin): list_display = ('customer', 'notice', 'receipt', 'created_at', 'expires_at', 'revoked_at', 'view_count') list_filter = ('revoked_at', 'expires_at') exclude = ('token_hash',) readonly_fields = ('customer', 'notice', 'receipt', 'created_at', 'expires_at', 'revoked_at', 'view_count', 'last_used_at', 'submitted_receipts') actions = ['revoke_links'] def has_add_permission(self, request): return False def has_delete_permission(self, request, obj=None): return False @admin.action(description='ابطال لینک‌های انتخاب‌شده') def revoke_links(self, request, queryset): from django.utils import timezone for link in queryset.filter(revoked_at__isnull=True): link.revoked_at = timezone.now() link.save(update_fields=['revoked_at']) services.audit(request.user, 'PAYMENT_LINK_REVOKED', link) from .forms import BankAccountForm class AuditedConfigurationAdmin(admin.ModelAdmin): permission = 'manage_payment_approval_policy' singleton_configuration = False def has_view_permission(self, request, obj=None): return policies.has_permission(request.user,self.permission) def has_add_permission(self, request): return self.has_view_permission(request) and not (self.singleton_configuration and self.model.objects.exists()) def has_change_permission(self, request, obj=None): return self.has_view_permission(request,obj) def has_delete_permission(self, request, obj=None): return False def save_model(self, request, obj, form, change): if not self.has_change_permission(request,obj): raise PermissionDenied current = self.model.objects.select_for_update().get(pk=obj.pk) if change else None before = services.configuration_snapshot(current) if current else {} super().save_model(request,obj,form,change) services.audit(request.user,'CONFIG_SAVED',obj,{'fields': form.changed_data, 'before':before,'after':services.configuration_snapshot(obj)}) def save_related(self, request, form, formsets, change): super().save_related(request,form,formsets,change) if isinstance(form.instance,models.PaymentApprovalPolicy): services.audit(request.user,'POLICY_ASSIGNMENTS_CHANGED',form.instance,{'reviewers':list(form.instance.reviewers.values_list('pk',flat=True)), 'seniors':list(form.instance.seniors.values_list('pk',flat=True))}) @admin.register(models.PaymentApprovalPolicy) class ApprovalPolicyAdmin(AuditedConfigurationAdmin): list_display = ['name','unit','is_active'] filter_horizontal = ['reviewers','seniors'] def formfield_for_manytomany(self, db_field, request, **kwargs): from apps.accounts.models import User if db_field.name in ['reviewers','seniors']: kwargs['queryset'] = User.objects.filter(account_type='STAFF',is_active=True).exclude(staff_profile__is_active=False) return super().formfield_for_manytomany(db_field,request,**kwargs) @admin.register(models.PaymentAccessConfiguration) class AccessAdmin(AuditedConfigurationAdmin): permission = 'manage_payment_access' singleton_configuration = True @admin.register(models.CompanyBankAccount) class AccountAdmin(AuditedConfigurationAdmin): form = BankAccountForm list_display = ['name','bank','requires_deposit_id','is_active','available_to_staff','available_to_customers'] permission = 'manage_company_bankaccounts' def has_view_permission(self, request, obj=None): return policies.can_manage_banks(request.user) def get_queryset(self, request): queryset = super().get_queryset(request) # Django wraps POST change forms in atomic(): lock before token validation. if request.method == 'POST' and connection.in_atomic_block: return queryset.select_for_update(of=('self',)) return queryset @admin.register(models.BankInstitution) class BankAdmin(AccountAdmin): form = admin.ModelAdmin.form list_display = ['name','code','is_active','historical'] class EvidenceAdmin(admin.ModelAdmin): def has_view_permission(self, request, obj=None): if self.model in [models.PaymentNotice, models.PaymentReceipt]: return super().has_view_permission(request, obj) return policies.has_permission(request.user, 'view_payment_audit') 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 def get_queryset(self, request): queryset = super().get_queryset(request) if queryset.model in [models.PaymentNotice,models.PaymentReceipt]: return policies.visible(queryset,request.user) return queryset if policies.has_permission(request.user,'view_payment_audit') else queryset.none() for model in [models.PaymentNotice,models.PaymentReceipt,models.PaymentReceiptRevision,models.ReceiptApprovalCycle, models.ReceiptApprovalAssignment,models.ReceiptDecision,models.PaymentAuditEvent, models.PaymentNotification,models.PaymentNotificationAttempt]: admin.site.register(model,EvidenceAdmin)