Files
MathApp/subscription/models.py
Zareian 10ad1e232c let the subscription card come from the server
کارتِ اشتراک زیرِ هر پلن فهرستِ امکانات و یک برچسب («محبوب‌ترین»، «بهترین
ارزش») نشان می‌دهد، و هر دو در کدِ فرانت هاردکد بودند — یعنی عوض کردنِ یک
بندِ متنی به دیپلویِ تازه‌ی اپ نیاز داشت.

Plan حالا features و badge دارد. `popular` سرِ جایش ماند: بولین است و متنِ
برچسب را نمی‌دهد، پس این جایگزینش نیست، کنارش است. هیچ‌کدام اجباری نیستند و
پلن‌های موجود بدونشان همچنان سرو می‌شوند.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 02:03:15 +03:30

60 lines
3.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from django.db import models
class Plan(models.Model):
"""
⚠️ اصلاح نسبت به سند اولیه: نمونه‌ی پاسخ GET /api/subscription/plans در
سند Endpointهای API یک فیلد boolean به اسم `popular` داره («ماهانه» →
false، «سالانه» → true) که در دیاگرام مدل‌ها (سند معماری داده) برای
Plan اصلاً ذکر نشده. چون بدون این فیلد نمی‌شه دقیقاً همون response
schema رو پیاده‌سازی کرد، اینجا اضافه شده — دقیقاً مثل فیکس is_correct
روی content.Choice در فاز ۱.
"""
title = models.CharField(max_length=50)
price = models.PositiveIntegerField()
currency = models.CharField(max_length=10, default='IRT')
discount_percent = models.PositiveSmallIntegerField(default=0)
popular = models.BooleanField(default=False)
# منبعِ صریحِ مدتِ اشتراک؛ بدونش activate_subscription مجبور بود مدت رو
# از رویِ متنِ title (مثلاً کلمه‌ی «سالانه») حدس بزنه.
duration_days = models.PositiveIntegerField(default=30)
# کارتِ اشتراک زیرِ هر پلن فهرستِ امکانات نشان می‌دهد («درس‌های نامحدود»،
# «دستیار هوشمند»…). تا امروز این فهرست در کدِ فرانت هاردکد بود، یعنی
# عوض کردنِ یک بند به دیپلویِ تازه‌ی اپ نیاز داشت.
features = models.JSONField(default=list, blank=True)
# برچسبِ بالای کارت: «محبوب‌ترین»، «بهترین ارزش». `popular` فقط بولین است
# و یک متن نمی‌دهد؛ این جایگزینش نیست، کنارش است.
badge = models.CharField(max_length=30, blank=True)
def __str__(self):
return self.title
class Subscription(models.Model):
"""وضعیتِ pending برای فاصله‌ی بینِ ساختنِ Subscription (در purchase) و
تاییدِ پرداخت (webhook)؛ فعال‌سازیِ واقعی توسطِ
subscription.services.activate_subscription انجام می‌شه."""
STATUS_CHOICES = [
('pending', 'در انتظار پرداخت'),
('active', 'فعال'),
('expired', 'منقضی'),
('canceled', 'لغوشده'),
]
user_id = models.IntegerField(unique=True, db_index=True) # ارجاع سبک به accounts.User
plan = models.ForeignKey(Plan, on_delete=models.SET_NULL, null=True, related_name='subscriptions')
status = models.CharField(max_length=15, choices=STATUS_CHOICES, default='pending')
started_at = models.DateTimeField(auto_now_add=True)
expires_at = models.DateTimeField(null=True, blank=True)
order_id = models.IntegerField(null=True, blank=True) # ارجاع سبک به payment.Order
# چون Subscription فقط یک ردیف در ازای هر user_id داره، دو خریدِ هم‌زمانِ
# همون کاربر می‌تونن به وضعیتِ میانیِ کاملاً یکسانی برسن (مقایسه‌ی
# فیلدها کافی نیست برای CAS). version با UPDATEِ اتمیکِ
# version=F('version')+1 (بدونِ select_for_update) هر نوشتنِ واقعی رو
# از نوشتنِ هم‌زمانِ دیگری تمیز می‌ده.
version = models.PositiveIntegerField(default=0)
def __str__(self):
return f"sub-user{self.user_id}-{self.status}"