forked from MehdiMstv/Saku-Backend
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #38 from Saku-SE/develop
Develop: merge onto main for releasing subscription and wallet feature on server
- Loading branch information
Showing
21 changed files
with
444 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -61,7 +61,8 @@ | |
"comment", | ||
"homepage", | ||
"chat", | ||
"support" | ||
"support", | ||
"subscription", | ||
] | ||
|
||
MIDDLEWARE = [ | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
from django.contrib import admin | ||
from .models import Subscription | ||
|
||
admin.site.register(Subscription) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
from django.apps import AppConfig | ||
|
||
|
||
class SubscriptionConfig(AppConfig): | ||
default_auto_field = 'django.db.models.BigAutoField' | ||
name = 'subscription' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
# Generated by Django 4.0.5 on 2023-05-16 19:01 | ||
|
||
from django.db import migrations, models | ||
|
||
|
||
class Migration(migrations.Migration): | ||
|
||
initial = True | ||
|
||
dependencies = [ | ||
] | ||
|
||
operations = [ | ||
migrations.CreateModel( | ||
name='Subscription', | ||
fields=[ | ||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), | ||
('name', models.CharField(max_length=40, unique=True)), | ||
('description', models.CharField(blank=True, max_length=200)), | ||
('duration', models.IntegerField()), | ||
('price', models.IntegerField()), | ||
], | ||
), | ||
] |
18 changes: 18 additions & 0 deletions
18
saku/subscription/migrations/0002_rename_duration_subscription_usage_limit.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
# Generated by Django 4.0.5 on 2023-05-17 11:19 | ||
|
||
from django.db import migrations | ||
|
||
|
||
class Migration(migrations.Migration): | ||
|
||
dependencies = [ | ||
('subscription', '0001_initial'), | ||
] | ||
|
||
operations = [ | ||
migrations.RenameField( | ||
model_name='subscription', | ||
old_name='duration', | ||
new_name='usage_limit', | ||
), | ||
] |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
from django.db import models | ||
|
||
class Subscription(models.Model): | ||
name = models.CharField(max_length=40, blank=False, null=False, unique=True) | ||
description = models.CharField(max_length=200, blank=True) | ||
usage_limit = models.IntegerField(null=False, blank=False) | ||
price = models.IntegerField(null=False, blank=False) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
from rest_framework import serializers | ||
from .models import Subscription | ||
|
||
class SubscriptionSerializer(serializers.ModelSerializer): | ||
class Meta: | ||
model = Subscription | ||
fields = "__all__" | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
import json | ||
|
||
from django.contrib.auth.models import User | ||
from django.urls import reverse | ||
from rest_framework import status | ||
from rest_framework.exceptions import ErrorDetail | ||
from rest_framework.test import APIClient, APITestCase | ||
|
||
from .models import Subscription | ||
from user_profile.models import Profile | ||
|
||
|
||
class SubscriptionTest(APITestCase): | ||
def setUp(self): | ||
self.client = APIClient() | ||
self.user = User.objects.create_user( | ||
username="test_user", password="Ab654321", email="email@email.com" | ||
) | ||
self.user.is_active = True | ||
self.user.save() | ||
self.subscription1 = Subscription.objects.create( | ||
name="name1", description="description1", usage_limit=10, price=15 | ||
) | ||
self.subscription2 = Subscription.objects.create( | ||
name="name2", description="description2", usage_limit=15, price=40 | ||
) | ||
self.profile = Profile.objects.create(user=self.user, email=self.user.email, wallet=30) | ||
self.client.force_authenticate(self.user) | ||
|
||
def test_subscription_list_view(self): | ||
url = reverse("subscription:subscription-list") | ||
|
||
response = self.client.get(url, format="json") | ||
self.assertEqual(status.HTTP_200_OK, response.status_code) | ||
self.assertEqual(len(response.data), 2) | ||
|
||
def test_purchase_subscription_success(self): | ||
url = reverse("subscription:purchase-subscription") | ||
data = {"id": self.subscription1.id} | ||
|
||
response = self.client.post(url, data, format="json") | ||
self.assertEqual(status.HTTP_200_OK, response.status_code) | ||
self.assertEqual(response.data["data"]["type"], self.subscription1.name) | ||
|
||
def test_purchase_subscription_invalid_purchase_already_active(self): | ||
url = reverse("subscription:purchase-subscription") | ||
data = {"id": self.subscription1.id} | ||
|
||
response = self.client.post(url, data, format="json") | ||
self.assertEqual(status.HTTP_200_OK, response.status_code) | ||
self.assertEqual(response.data["data"]["type"], self.subscription1.name) | ||
|
||
response = self.client.post(url, data, format="json") | ||
self.assertEqual(status.HTTP_400_BAD_REQUEST, response.status_code) | ||
self.assertEqual(response.data["message"], "Invalid purchase") | ||
|
||
def test_purchase_subscription_insufficient_funds(self): | ||
url = reverse("subscription:purchase-subscription") | ||
data = {"id": self.subscription2.id} | ||
|
||
response = self.client.post(url, data, format="json") | ||
self.assertEqual(status.HTTP_400_BAD_REQUEST, response.status_code) | ||
self.assertEqual(response.data["message"], "Insufficient funds") | ||
|
||
def test_user_active_subscription_empty(self): | ||
url = reverse("subscription:user-active-subscription") | ||
|
||
response = self.client.get(url, format="json") | ||
self.assertEqual(status.HTTP_204_NO_CONTENT, response.status_code) | ||
|
||
def test_user_active_subscription_not_empty(self): | ||
url = reverse("subscription:purchase-subscription") | ||
data = {"id": self.subscription1.id} | ||
|
||
response = self.client.post(url, data, format="json") | ||
self.assertEqual(status.HTTP_200_OK, response.status_code) | ||
self.assertEqual(response.data["data"]["type"], self.subscription1.name) | ||
|
||
url = reverse("subscription:user-active-subscription") | ||
|
||
response = self.client.get(url, format="json") | ||
self.assertEqual(status.HTTP_200_OK, response.status_code) | ||
self.assertEqual(response.data["id"], self.subscription1.id) | ||
self.assertEqual(response.data["name"], self.subscription1.name) | ||
self.assertEqual(response.data["left_time_in_days"], 30) | ||
|
||
|
||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
from django.urls import path | ||
from .views import SubscriptionListView, PurchaseSubscriptionView, UserActiveSubscriptionInfoView | ||
|
||
app_name = "subscription" | ||
urlpatterns = [ | ||
path("list", SubscriptionListView.as_view(), name="subscription-list"), | ||
path("purchase", PurchaseSubscriptionView.as_view(), name="purchase-subscription"), | ||
path("active", UserActiveSubscriptionInfoView.as_view(), name="user-active-subscription") | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
from rest_framework import generics, status | ||
from rest_framework.permissions import IsAuthenticated | ||
from rest_framework.response import Response | ||
from .models import Subscription | ||
from user_profile.models import Profile | ||
from .serializers import SubscriptionSerializer | ||
from django.shortcuts import get_object_or_404 | ||
import datetime | ||
from django.db import transaction | ||
|
||
class SubscriptionListView(generics.ListAPIView): | ||
permission_classes = (IsAuthenticated,) | ||
queryset = Subscription.objects.all() | ||
serializer_class = SubscriptionSerializer | ||
|
||
class PurchaseSubscriptionView(generics.GenericAPIView): | ||
permission_classes = (IsAuthenticated,) | ||
|
||
def post(self, request, *args, **kwargs): | ||
profile = Profile.objects.get(user=request.user) | ||
if not profile.subscription is None: | ||
return Response({ | ||
"message": "Invalid purchase", | ||
"detail": "You have an already active purchase." | ||
}, status=status.HTTP_400_BAD_REQUEST) | ||
subscription = get_object_or_404(Subscription, id=request.data['id']) | ||
if profile.wallet < subscription.price: | ||
return Response({ | ||
"message": "Insufficient funds", | ||
"detail": "You don't have enough credit in your wallet" | ||
}, status=status.HTTP_400_BAD_REQUEST) | ||
|
||
with transaction.atomic(): | ||
profile.wallet -= subscription.price | ||
profile.subscription = subscription | ||
profile.subscription_date = datetime.datetime.now() | ||
profile.save() | ||
return Response({ | ||
"status": "success", | ||
"code": status.HTTP_200_OK, | ||
"data": { | ||
"type": subscription.name, | ||
"usage_limit": subscription.usage_limit, | ||
"left_time_in_days": 30, | ||
} | ||
}, status=status.HTTP_200_OK) | ||
|
||
class UserActiveSubscriptionInfoView(generics.GenericAPIView): | ||
permission_classes = (IsAuthenticated,) | ||
|
||
def get(self, request): | ||
profile = Profile.objects.get(user=request.user) | ||
active_subscription = profile.subscription | ||
if active_subscription is None: | ||
return Response({}, status=status.HTTP_204_NO_CONTENT) | ||
return Response({ | ||
"id": active_subscription.id, | ||
"name": active_subscription.name, | ||
"usage_limit": active_subscription.usage_limit, | ||
"left_time_in_days": 30 - (datetime.datetime.now().date() - profile.subscription_date.date()).days | ||
}, status=status.HTTP_200_OK) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
# Generated by Django 4.0.5 on 2023-05-16 16:17 | ||
|
||
from django.db import migrations, models | ||
|
||
|
||
class Migration(migrations.Migration): | ||
|
||
dependencies = [ | ||
('user_profile', '0007_followrelationship_and_more'), | ||
] | ||
|
||
operations = [ | ||
migrations.AddField( | ||
model_name='profile', | ||
name='wallet', | ||
field=models.IntegerField(default=0), | ||
), | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
# Generated by Django 4.0.5 on 2023-05-16 19:22 | ||
|
||
from django.db import migrations, models | ||
import django.db.models.deletion | ||
|
||
|
||
class Migration(migrations.Migration): | ||
|
||
dependencies = [ | ||
('subscription', '0001_initial'), | ||
('user_profile', '0008_profile_wallet'), | ||
] | ||
|
||
operations = [ | ||
migrations.AddField( | ||
model_name='profile', | ||
name='subscription', | ||
field=models.ForeignKey(default=None, null=True, on_delete=django.db.models.deletion.SET_NULL, to='subscription.subscription'), | ||
), | ||
] |
18 changes: 18 additions & 0 deletions
18
saku/user_profile/migrations/0010_profile_subscription_date.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
# Generated by Django 4.0.5 on 2023-05-17 11:41 | ||
|
||
from django.db import migrations, models | ||
|
||
|
||
class Migration(migrations.Migration): | ||
|
||
dependencies = [ | ||
('user_profile', '0009_profile_subscription'), | ||
] | ||
|
||
operations = [ | ||
migrations.AddField( | ||
model_name='profile', | ||
name='subscription_date', | ||
field=models.DateTimeField(blank=True, default=None, null=True), | ||
), | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.