Files
MathApp/discount/models.py

101 lines
4.9 KiB
Python

from django.db import models
class DiscountCode(models.Model):
TYPE_CHOICES = [('percent', 'درصدی'), ('fixed', 'مبلغ ثابت')]
code = models.CharField(max_length=30, unique=True)
type = models.CharField(max_length=10, choices=TYPE_CHOICES)
value = models.PositiveIntegerField()
max_uses = models.PositiveIntegerField(default=1)
expires_at = models.DateTimeField(null=True, blank=True)
def __str__(self):
return self.code
class Redemption(models.Model):
discount_code = models.ForeignKey(DiscountCode, on_delete=models.CASCADE, related_name='redemptions')
user_id = models.IntegerField(db_index=True) # ارجاع سبک به accounts.User
order_id = models.IntegerField() # ارجاع سبک به payment.Order
redeemed_at = models.DateTimeField(auto_now_add=True)
class Meta:
# هر کاربر برای یک کد تخفیف حداکثر یک‌بار می‌تونه redeem کنه؛
# قبلاً هیچ محدودیتی در سطح دیتابیس نبود و همراه با نبود چک max_uses
# در RedeemDiscountView، امکان استفاده‌ی نامحدود از یک کد وجود داشت.
constraints = [
models.UniqueConstraint(
fields=['discount_code', 'user_id'], name='unique_redemption_per_user_per_code'
)
]
def __str__(self):
return f"redemption-{self.discount_code_id}-order{self.order_id}"
class Offer(models.Model):
"""
آفر رایگان/تخفیف مستقل از DiscountCode: برخلاف DiscountCode که روی
مبلغِ یک سفارش اثر می‌ذاره، Offer دسترسی (اشتراک) رایگان می‌ده — یا
برای مدت محدود ('trial'، مثلاً یک هفته بعد از ثبت‌نام) یا کامل و
نامحدود ('free_full'). طبق درخواست سینیور، اعمال این آفرها signal-based
و قابل‌تغییره: کدوم آفر، با چه شرطی، روی کدوم کاربر اعمال بشه بدون
دست‌زدن به accounts یا subscription قابل تغییره (فقط discount/receivers.py
و discount/services.py).
"""
KIND_CHOICES = [
('trial', 'دوره‌ی رایگان محدود'),
('free_full', 'دسترسی کامل رایگان'),
]
name = models.CharField(max_length=100)
kind = models.CharField(max_length=10, choices=KIND_CHOICES)
# فقط برای kind='trial' معنی داره؛ برای free_full خالی می‌مونه (یعنی نامحدود).
duration_days = models.PositiveIntegerField(null=True, blank=True)
# اگه پر باشه، کاربر می‌تونه بعداً (نه فقط هنگام ثبت‌نام) با این کد آفر
# رو خودش اعمال کنه؛ دقیقاً مشابه DiscountCode.code ولی برای اشتراک،
# نه تخفیفِ یک سفارش.
code = models.CharField(max_length=30, unique=True, null=True, blank=True)
# اگه True باشه، همون لحظه‌ی signal اول (accounts.signals.user_registered)
# بدون نیاز به وارد کردن کد، خودکار به کاربر تازه‌ثبت‌نام‌شده اختصاص داده می‌شه.
auto_assign_on_signup = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.name
class UserOffer(models.Model):
"""ثبتِ اینکه کدوم آفر به کدوم کاربر، از چه مسیری، چه زمانی اختصاص داده شده."""
SOURCE_CHOICES = [
('signup_auto', 'خودکار هنگام ثبت‌نام'),
('code', 'وارد کردن کد'),
('manual', 'اختصاص دستی/پشتیبانی'),
]
STATUS_CHOICES = [
('active', 'فعال'),
('expired', 'منقضی'),
('revoked', 'لغوشده'),
]
user_id = models.IntegerField(db_index=True) # ارجاع سبک به accounts.User
offer = models.ForeignKey(Offer, on_delete=models.CASCADE, related_name='user_offers')
source = models.CharField(max_length=15, choices=SOURCE_CHOICES)
status = models.CharField(max_length=10, choices=STATUS_CHOICES, default='active')
granted_at = models.DateTimeField(auto_now_add=True)
expires_at = models.DateTimeField(null=True, blank=True) # None یعنی نامحدود (free_full)
class Meta:
ordering = ['-granted_at']
# هر کاربر برای یک آفرِ مشخص فقط یک‌بار می‌تونه دریافت کنه (چه
# auto چه با کد)؛ جلوی گرفتن مکرر یک آفرِ trial رو می‌گیره.
constraints = [
models.UniqueConstraint(fields=['offer', 'user_id'], name='unique_offer_per_user')
]
def __str__(self):
return f"useroffer-{self.offer_id}-user{self.user_id}-{self.status}"