# management/models.py # -*- coding: utf-8 -*- from django.db import models from django.contrib.auth.models import AbstractUser from django.utils import timezone import uuid import datetime # --- Users & Authentication --- class CustomUser(AbstractUser): ROLE_CHOICES = [ ('SuperAdmin', 'Super Admin'), ('PanelOwner', 'Panel Owner'), ('AdminLevel2', 'Admin Level 2'), ('AdminLevel3', 'Admin Level 3'), ('EndUser', 'End User'), ] role = models.CharField(max_length=20, choices=ROLE_CHOICES, default='EndUser') marzban_admin_id = models.UUIDField(null=True, blank=True) def is_super_admin(self): return self.role == 'SuperAdmin' def is_admin_level_2(self): return self.role in ('SuperAdmin', 'PanelOwner', 'AdminLevel2') def is_admin_level_3(self): return self.role in ('SuperAdmin', 'PanelOwner', 'AdminLevel2', 'AdminLevel3') def __str__(self): return self.username # --- Main Website Models --- class AdminLevel2(models.Model): user = models.OneToOneField( CustomUser, on_delete=models.CASCADE, related_name='admin_level_2' ) telegram_chat_id = models.CharField( max_length=255, blank=True, null=True, verbose_name="تلگرام چت آیدی" ) license_expiry_date = models.DateField( null=True, blank=True, verbose_name="تاریخ انقضای لایسنس" ) def __str__(self): return f"Admin Level 2: {self.user.username}" class AdminLevel3(models.Model): user = models.OneToOneField( CustomUser, on_delete=models.CASCADE, related_name='admin_level_3' ) parent_admin = models.ForeignKey( AdminLevel2, on_delete=models.CASCADE, related_name='child_admins', verbose_name="ادمین والد (سطح ۲)" ) telegram_chat_id = models.CharField( max_length=255, blank=True, null=True, verbose_name="تلگرام چت آیدی" ) def __str__(self): return f"Admin Level 3: {self.user.username}" class Panel(models.Model): owner = models.OneToOneField(CustomUser, on_delete=models.CASCADE, related_name='owned_panel') name = models.CharField(max_length=255, verbose_name="نام پنل") marzban_host = models.CharField(max_length=255, verbose_name="آدرس هاست Marzban") marzban_username = models.CharField(max_length=255, verbose_name="نام کاربری Marzban") marzban_password = models.CharField(max_length=255, verbose_name="رمز عبور Marzban") telegram_bot_token = models.CharField(max_length=255, blank=True, null=True) # ADDED: Link to the license license = models.OneToOneField('License', on_delete=models.SET_NULL, null=True, blank=True, related_name='linked_panel') def __str__(self): return self.name class License(models.Model): key = models.UUIDField(default=uuid.uuid4, editable=False, unique=True, verbose_name="کلید لایسنس") is_active = models.BooleanField(default=True, verbose_name="وضعیت فعال") owner = models.ForeignKey(CustomUser, on_delete=models.CASCADE, verbose_name="صاحب لایسنس") created_at = models.DateTimeField(auto_now_add=True, verbose_name="تاریخ ایجاد") expiry_date = models.DateField(verbose_name="تاریخ انقضا") def __str__(self): return str(self.key) # ADDED: MarzbanAdmin model (was missing) class MarzbanAdmin(models.Model): username = models.CharField(max_length=255, unique=True) password = models.CharField(max_length=255) permission = models.CharField(max_length=50) panel = models.ForeignKey(Panel, on_delete=models.CASCADE, related_name='marzban_admins') def __str__(self): return self.username # ADDED: MarzbanUser model (was missing) class MarzbanUser(models.Model): username = models.CharField(max_length=255, unique=True) # Add other fields relevant to Marzban users # ... def __str__(self): return self.username class EndUser(models.Model): username = models.CharField(max_length=255, unique=True) panel = models.ForeignKey(Panel, on_delete=models.CASCADE) # marzban_user_id can be nullable if the user is created first in our DB marzban_user_id = models.CharField(max_length=255, unique=True, null=True, blank=True) telegram_chat_id = models.CharField(max_length=255, blank=True, null=True) def __str__(self): return self.username # ADDED: Plan model (was missing) class Plan(models.Model): name = models.CharField(max_length=100) price = models.DecimalField(max_digits=10, decimal_places=2) duration_days = models.IntegerField() data_limit_gb = models.FloatField() is_active = models.BooleanField(default=True) panel = models.ForeignKey(Panel, on_delete=models.CASCADE, related_name='plans') def __str__(self): return f"{self.name} ({self.panel.name})" # ADDED: Subscription model (was missing) class Subscription(models.Model): STATUS_CHOICES = [ ('active', 'فعال'), ('expired', 'منقضی شده'), ('pending', 'در انتظار پرداخت'), ('deactivated', 'غیرفعال'), ] end_user = models.ForeignKey(EndUser, on_delete=models.CASCADE, related_name='subscriptions') plan = models.ForeignKey(Plan, on_delete=models.PROTECT) # Don't delete plan if subscription exists panel = models.ForeignKey(Panel, on_delete=models.CASCADE, related_name='subscriptions') status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='pending') start_date = models.DateField(null=True, blank=True) end_date = models.DateField(null=True, blank=True) remaining_data_gb = models.FloatField(null=True, blank=True) def __str__(self): return f"Subscription for {self.end_user.username} on plan {self.plan.name}" # FIXED: Complete overhaul of the Payment model class Payment(models.Model): STATUS_CHOICES = [ ('pending', 'در انتظار تأیید'), ('approved', 'تأیید شده'), ('rejected', 'رد شده'), ] subscription = models.ForeignKey(Subscription, on_delete=models.CASCADE, related_name='payments') admin = models.ForeignKey(CustomUser, on_delete=models.PROTECT, related_name='handled_payments') # The admin to approve amount = models.DecimalField(max_digits=10, decimal_places=2, verbose_name="مبلغ") status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='pending', verbose_name="وضعیت") created_at = models.DateTimeField(auto_now_add=True, verbose_name="تاریخ ایجاد") # Fields for receipt upload payment_token = models.UUIDField(default=uuid.uuid4, editable=False, unique=True) receipt_image = models.ImageField(upload_to='receipts/', blank=True, null=True, verbose_name="تصویر رسید") receipt_text = models.TextField(blank=True, null=True, verbose_name="متن رسید") def __str__(self): return f"Payment for {self.subscription.end_user.username} - {self.amount} - {self.status}" class DiscountCode(models.Model): code = models.CharField(max_length=50, unique=True) admin = models.ForeignKey(CustomUser, on_delete=models.CASCADE) discount_percentage = models.DecimalField(max_digits=5, decimal_places=2) is_active = models.BooleanField(default=True) def __str__(self): return self.code # --- Telegram Integration --- class TelegramUser(models.Model): admin_id = models.CharField(max_length=255, unique=True, primary_key=True) chat_id = models.CharField(max_length=255, unique=True) username = models.CharField(max_length=255) class Meta: managed = False db_table = 'telegram_users' def __str__(self): return self.username class SecurityToken(models.Model): admin_id = models.CharField(max_length=255, primary_key=True) token = models.CharField(max_length=255, unique=True, default=uuid.uuid4) expiration_date = models.DateTimeField() class Meta: managed = False db_table = 'telegram_tokens' def __str__(self): return f"Token for {self.admin_id}" # --- Push Notifications --- class PushSubscription(models.Model): user = models.ForeignKey( CustomUser, on_delete=models.CASCADE, related_name='push_subscriptions' ) subscription_info = models.JSONField() created_at = models.DateTimeField(auto_now_add=True) # --- Payment System Models --- class PaymentMethod(models.Model): METHOD_CHOICES = [ ('gateway', 'Bank Gateway'), ('crypto', 'Cryptocurrency'), ('manual', 'Manual (Card-to-Card)'), ] name = models.CharField(max_length=50, choices=METHOD_CHOICES, unique=True, verbose_name="Payment Method Name") is_active = models.BooleanField(default=True, verbose_name="Is Active?") can_be_managed_by_level3 = models.BooleanField(default=False, verbose_name="Can be managed by Admin Level 3?") class Meta: verbose_name = "Payment Method" verbose_name_plural = "Payment Methods" def __str__(self): return self.get_name_display() class PaymentSetting(models.Model): admin_level_3 = models.ForeignKey( CustomUser, on_delete=models.CASCADE, limit_choices_to={'role': 'AdminLevel3'}, related_name='payment_settings' ) payment_method = models.ForeignKey(PaymentMethod, on_delete=models.CASCADE) is_active = models.BooleanField(default=False, verbose_name="Is Active for this Admin?") class Meta: unique_together = ('admin_level_3', 'payment_method') verbose_name = "Payment Setting" verbose_name_plural = "Payment Settings" def __str__(self): return f"{self.admin_level_3.username}'s {self.payment_method.get_name_display()} Setting" class PaymentDetail(models.Model): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) admin_level_3 = models.OneToOneField( CustomUser, on_delete=models.CASCADE, limit_choices_to={'role': 'AdminLevel3'}, related_name='payment_details' ) card_number = models.CharField(max_length=50, blank=True, null=True) card_holder_name = models.CharField(max_length=255, blank=True, null=True) wallet_address = models.CharField(max_length=255, blank=True, null=True) class Meta: verbose_name = "Payment Detail" verbose_name_plural = "Payment Details" def __str__(self): return f"Payment details for {self.admin_level_3.username}"