Files
2026-10-10 14:21:04 +03:30

178 lines
7.2 KiB
Python

from django.contrib import admin
# Register your models here.
from .models import *
admin.site.register(Customer)
admin.site.register(PhoneNumber)
admin.site.register(ProductCategory)
admin.site.register(Product)
admin.site.register(ProductInStore)
admin.site.register(Factor)
admin.site.register(FactorIteam)
admin.site.register(VisitQueue)
admin.site.register(ArticleType)
admin.site.register(Article)
admin.site.register(Sarresid)
admin.site.register(Action)
admin.site.register(CacheTable)
admin.site.register(ProductStockHistory)
@admin.register(StoreTransferRequest)
class StoreTransferRequestAdmin(admin.ModelAdmin):
# ۱. حل مشکل لودینگ و هنگ کردن فرم (تبدیل منوی کشویی غول‌آسا به باکس شناسه عددی)
# این دستور فیلدهای پرجمعیت را به شدت بهینه و سبک می‌کند
raw_id_fields = ['out_transaction', 'in_transaction', 'product', 'requester', 'approver']
# ۲. ستون‌هایی که می‌خواهید در صفحه جدول (Changelist) نمایش داده شوند
list_display = [
'id',
'source_store',
'target_store',
'product',
'quantity',
'status',
'created_at',
'action_date'
]
# ۳. فیکس قطعی باگ N+1 (لود کردن روابط در یک کوئری واحد به جای صدها کوئری تودرتو برای __str__)
list_select_related = ['source_store', 'target_store', 'product', 'requester', 'approver']
# ۴. فیلترهای سمت راست صفحه (فقط روی فیلدهای کم‌جمعیت مثل وضعیت و انبار فیلتر بگذارید)
list_filter = ['status', 'source_store', 'target_store', 'created_at']
# ۵. باکس جستجوی بهینه شده
search_fields = ['id', 'desc', 'product__product_name', 'requester__username']
# ۶. غیرقابل ادیت کردن فیلدهای حساس مالی در پنل ادمین (اختیاری اما برای Enterprise الزامی است)
readonly_fields = ['created_at', 'updated_at']
class StoreRoomOwnerInline(admin.TabularInline):
"""
این کلاس باعث می‌شود مالکین انبار به صورت یک جدول کوچک
در داخل همان صفحه مدیریت انبار نمایش داده شوند.
"""
model = StoreRoomOwner
extra = 1 # تعداد ردیف‌های خالی برای افزودن مالک جدید
verbose_name = "مالک انبار"
verbose_name_plural = "مالکین انبار"
@admin.register(StoreRoom)
class StoreRoomAdmin(admin.ModelAdmin):
list_display = ('store_name', 'get_owners_count')
search_fields = ('store_name',)
# فیلد user یک M2M معمولی است، پس filter_horizontal روی آن عالی کار می‌کند
filter_horizontal = ('user',)
# فیلد owners چون جدول واسط دارد، باید از طریق Inline مدیریت شود
inlines = [StoreRoomOwnerInline]
def get_owners_count(self, obj):
"""نمایش تعداد مالکین در لیست اصلی انبارها"""
return obj.owners.count()
get_owners_count.short_description = "تعداد مالکین"
# ============================================================
# مدیریت پیامک (SMS)
# ============================================================
from django import forms
from django.contrib import messages
class SMSProviderConfigForm(forms.ModelForm):
class Meta:
model = SMSProviderConfig
fields = '__all__'
widgets = {
'password': forms.PasswordInput(render_value=True),
'api_token': forms.PasswordInput(render_value=True),
}
@admin.register(SMSProviderConfig)
class SMSProviderConfigAdmin(admin.ModelAdmin):
form = SMSProviderConfigForm
list_display = ('provider', 'is_enabled', 'sender', 'username',
'allow_backlog', 'backlog_max', 'max_per_run',
'send_interval_seconds', 'updated_at')
readonly_fields = ('updated_at',)
fieldsets = (
("اتصال سرویس‌دهنده", {
'fields': ('provider', 'is_enabled', 'api_url', 'username', 'password',
'sender', 'api_token', 'test_message', 'timeout_seconds'),
}),
("وب‌سرویس ۰۰۹۸ (فقط provider وب‌سرویس)", {
'fields': ('webservice_url', 'panel_no'),
}),
("کنترل جریان ارسال", {
'fields': ('scan_since', 'allow_backlog', 'backlog_max',
'max_per_run', 'send_interval_seconds', 'updated_at'),
}),
)
def has_add_permission(self, request):
# سینگلتون: فقط ویرایش رکورد موجود.
return not SMSProviderConfig.objects.exists()
def has_delete_permission(self, request, obj=None):
return False
@admin.register(SMSTemplate)
class SMSTemplateAdmin(admin.ModelAdmin):
list_display = ('title', 'sms_type', 'is_enabled', 'auto_send', 'offset_days',
'repeat_interval_days', 'repeat_interval_months')
list_editable = ('is_enabled', 'auto_send')
list_filter = ('sms_type', 'is_enabled', 'auto_send')
search_fields = ('title', 'text')
readonly_fields = ('updated_at',)
@admin.register(CustomerSMSSetting)
class CustomerSMSSettingAdmin(admin.ModelAdmin):
list_display = ('customer', 'sms_type', 'is_enabled')
list_filter = ('sms_type', 'is_enabled')
raw_id_fields = ('customer',)
search_fields = ('customer__name_family',)
@admin.register(SMSQueue)
class SMSQueueAdmin(admin.ModelAdmin):
list_display = ('id', 'customer', 'sms_type', 'phone_number', 'status',
'delivery_status', 'attempts', 'created_at', 'sent_at')
list_filter = ('status', 'sms_type', 'delivery_status')
raw_id_fields = ('customer', 'created_by')
search_fields = ('phone_number', 'message', 'dedup_key')
readonly_fields = ('created_at', 'updated_at', 'sent_at')
actions = ['approve_selected', 'retry_selected']
@admin.action(description="تأیید موارد انتخاب‌شده (ارسال توسط worker)")
def approve_selected(self, request, queryset):
updated = queryset.filter(status=SMSQueue.Status.PENDING).update(
status=SMSQueue.Status.APPROVED, next_retry_at=None,
)
self.message_user(request, f"{updated} پیامک تأیید شد.", messages.SUCCESS)
@admin.action(description="ارسال مجدد موارد انتخاب‌شده")
def retry_selected(self, request, queryset):
updated = queryset.update(status=SMSQueue.Status.APPROVED, next_retry_at=None, last_error='')
self.message_user(request, f"{updated} پیامک برای ارسال مجدد تأیید شد.", messages.SUCCESS)
@admin.register(SMSLog)
class SMSLogAdmin(admin.ModelAdmin):
list_display = ('id', 'queue', 'attempt_no', 'success', 'provider_code', 'created_at')
list_filter = ('success',)
raw_id_fields = ('queue',)
readonly_fields = ('queue', 'attempt_no', 'success', 'provider_id', 'provider_code', 'error', 'created_at')
def has_add_permission(self, request):
return False
def has_change_permission(self, request, obj=None):
return False