diff --git a/subscription/migrations/0004_plan_badge_plan_features.py b/subscription/migrations/0004_plan_badge_plan_features.py new file mode 100644 index 0000000..26f6ae6 --- /dev/null +++ b/subscription/migrations/0004_plan_badge_plan_features.py @@ -0,0 +1,23 @@ +# Generated by Django 5.2.17 on 2026-09-13 22:30 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('subscription', '0003_subscription_version'), + ] + + operations = [ + migrations.AddField( + model_name='plan', + name='badge', + field=models.CharField(blank=True, max_length=30), + ), + migrations.AddField( + model_name='plan', + name='features', + field=models.JSONField(blank=True, default=list), + ), + ] diff --git a/subscription/models.py b/subscription/models.py index d6fe940..848914e 100644 --- a/subscription/models.py +++ b/subscription/models.py @@ -19,6 +19,14 @@ class Plan(models.Model): # از رویِ متنِ 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 diff --git a/subscription/serializers.py b/subscription/serializers.py index 0066b83..3573587 100644 --- a/subscription/serializers.py +++ b/subscription/serializers.py @@ -9,6 +9,8 @@ class PlanSerializer(serializers.Serializer): popular = serializers.BooleanField() discountPercent = serializers.IntegerField(required=False) durationDays = serializers.IntegerField(required=False) + features = serializers.ListField(child=serializers.CharField()) + badge = serializers.CharField(allow_blank=True) class PlansListResponseSerializer(serializers.Serializer): diff --git a/subscription/tests.py b/subscription/tests.py index 49ba75c..b02b3db 100644 --- a/subscription/tests.py +++ b/subscription/tests.py @@ -516,3 +516,28 @@ class PurchaseGuestThrottleBypassTests(TestCase): self.assertFalse(Subscription.objects.filter(user_id=guest2.id).exists()) self.assertEqual(Order.objects.filter(user_id=guest2.id).count(), 0) + + +class PlanCardContractTests(TestCase): + """ + کارتِ اشتراک در فرانت فهرستِ امکانات و یک برچسب نشان می‌دهد؛ تا امروز هر + دو در کدِ اپ هاردکد بودند، یعنی عوض کردنِ یک بند به دیپلویِ تازه نیاز داشت. + """ + + def setUp(self): + from django.core.cache import cache + cache.clear() + + def test_plans_carry_their_feature_list_and_badge(self): + Plan.objects.create(title='طلایی', price=49000, popular=True, + badge='محبوب‌ترین', + features=['درس‌های نامحدود', 'بدون تبلیغ']) + row = self.client.get('/api/subscription/plans').json()['plans'][0] + self.assertEqual(row['badge'], 'محبوب‌ترین') + self.assertEqual(row['features'], ['درس‌های نامحدود', 'بدون تبلیغ']) + + def test_a_plan_without_them_still_serializes(self): + Plan.objects.create(title='رایگان', price=0) + row = self.client.get('/api/subscription/plans').json()['plans'][0] + self.assertEqual(row['features'], []) + self.assertEqual(row['badge'], '') diff --git a/subscription/views.py b/subscription/views.py index 92c8b58..cd5df99 100644 --- a/subscription/views.py +++ b/subscription/views.py @@ -61,6 +61,8 @@ class PlansListView(APIView): 'popular': p.popular, 'discountPercent': p.discount_percent, 'durationDays': p.duration_days, + 'features': p.features or [], + 'badge': p.badge, } for p in Plan.objects.all() ]