Skip to content

Commit fef195d

Browse files
authored
Merge pull request #31 from Kalgoc/feat/bank-card
Bank Card
2 parents 3e13fe6 + ecf545f commit fef195d

15 files changed

+334
-6
lines changed

bankcard/__init__.py

Whitespace-only changes.

bankcard/admin.py

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
from django.contrib import admin
2+
from .models import BankCard
3+
4+
5+
@admin.register(BankCard)
6+
class BankCardAdmin(admin.ModelAdmin):
7+
list_display = (
8+
"id",
9+
"user_id",
10+
"account_number",
11+
"bank_name",
12+
"card_type",
13+
"created_at",
14+
"updated_at",
15+
)

bankcard/apps.py

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
from django.apps import AppConfig
2+
3+
4+
class BankCardConfig(AppConfig):
5+
default_auto_field = "django.db.models.BigAutoField"
6+
name = "bankcard"

bankcard/migrations/0001_initial.py

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# Generated by Django 5.0.6 on 2024-06-30 22:17
2+
3+
import django.db.models.deletion
4+
from django.conf import settings
5+
from django.db import migrations, models
6+
7+
8+
class Migration(migrations.Migration):
9+
10+
initial = True
11+
12+
dependencies = [
13+
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
14+
]
15+
16+
operations = [
17+
migrations.CreateModel(
18+
name="BankCard",
19+
fields=[
20+
("id", models.AutoField(primary_key=True, serialize=False)),
21+
("account_number", models.IntegerField()),
22+
("bank_name", models.CharField(max_length=128)),
23+
("card_type", models.CharField(max_length=128)),
24+
("created_at", models.DateTimeField(auto_now_add=True)),
25+
("updated_at", models.DateTimeField(auto_now=True)),
26+
(
27+
"user_id",
28+
models.ForeignKey(
29+
on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, to_field="user_id"
30+
),
31+
),
32+
],
33+
),
34+
]

bankcard/migrations/__init__.py

Whitespace-only changes.

bankcard/models.py

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
from django.db import models
2+
from django.conf import settings
3+
4+
5+
class BankCard(models.Model):
6+
id = models.AutoField(primary_key=True)
7+
user_id = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, to_field="user_id")
8+
account_number = models.IntegerField()
9+
bank_name = models.CharField(max_length=128)
10+
card_type = models.CharField(max_length=128)
11+
created_at = models.DateTimeField(auto_now_add=True)
12+
updated_at = models.DateTimeField(auto_now=True)

bankcard/serializers.py

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
from rest_framework import serializers
2+
from .models import BankCard
3+
4+
5+
class BankCardSerializer(serializers.ModelSerializer):
6+
class Meta:
7+
model = BankCard
8+
fields = "__all__"
9+
10+
def create(self, validated_data):
11+
return BankCard.objects.create(**validated_data)

bankcard/tests.py

+102
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
from django.test import TestCase
2+
from unittest.mock import patch
3+
from rest_framework.test import APIClient
4+
from rest_framework import status
5+
from authentication.services.cognito_service import CognitoService
6+
from rest_framework.response import Response
7+
from .views import BankCardViewSet
8+
9+
10+
class BankCardViewSetTestCase(TestCase):
11+
def setUp(self):
12+
self.client = APIClient()
13+
self.view = BankCardViewSet()
14+
self.card = {
15+
"account_number": 44467861,
16+
"bank_name": "Super Bank",
17+
"card_type": "credit",
18+
}
19+
20+
@patch.object(BankCardViewSet, "create")
21+
@patch.object(CognitoService, "login_user")
22+
def test_create(self, mock_login_user, mock_create):
23+
mock_login_user.return_value = {
24+
"AuthenticationResult": {"access_token": "mock_access_token", "id_token": "mock_id_token"}
25+
}
26+
mock_create.return_value = Response(status=status.HTTP_201_CREATED, data=self.card)
27+
28+
request = self.client.post("/bankcard/", self.card)
29+
request.headers["Authorization"] = "Bearer mock_access_token"
30+
31+
response = self.client.post("/bankcard/", self.card)
32+
33+
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
34+
self.assertEqual(response.data["account_number"], 44467861)
35+
36+
@patch.object(BankCardViewSet, "list")
37+
@patch.object(CognitoService, "login_user")
38+
def test_list(self, mock_login_user, mock_list):
39+
mock_login_user.return_value = {
40+
"AuthenticationResult": {"access_token": "mock_access_token", "id_token": "mock_id_token"}
41+
}
42+
mock_list.return_value = Response(status=status.HTTP_200_OK, data=[self.card])
43+
44+
request = self.client.get("/bankcard/")
45+
request.headers["Authorization"] = "Bearer mock_access_token"
46+
47+
response = self.client.get("/bankcard/")
48+
49+
self.assertEqual(response.status_code, status.HTTP_200_OK)
50+
self.assertEqual(response.data[0]["account_number"], 44467861)
51+
52+
@patch.object(BankCardViewSet, "retrieve")
53+
@patch.object(CognitoService, "login_user")
54+
def test_retrieve(self, mock_login_user, mock_retrieve):
55+
mock_login_user.return_value = {
56+
"AuthenticationResult": {"access_token": "mock_access_token", "id_token": "mock_id_token"}
57+
}
58+
mock_retrieve.return_value = Response(status=status.HTTP_200_OK, data=self.card)
59+
60+
request = self.client.get("/bankcard/1/")
61+
request.headers["Authorization"] = "Bearer mock_access_token"
62+
63+
response = self.client.get("/bankcard/1/")
64+
65+
self.assertEqual(response.status_code, status.HTTP_200_OK)
66+
self.assertEqual(response.data["account_number"], 44467861)
67+
68+
@patch.object(BankCardViewSet, "destroy")
69+
@patch.object(CognitoService, "login_user")
70+
def test_destroy(self, mock_login_user, mock_destroy):
71+
mock_login_user.return_value = {
72+
"AuthenticationResult": {"access_token": "mock_access_token", "id_token": "mock_id_token"}
73+
}
74+
mock_destroy.return_value = Response(status=status.HTTP_204_NO_CONTENT)
75+
76+
request = self.client.delete("/bankcard/1/")
77+
request.headers["Authorization"] = "Bearer mock_access_token"
78+
79+
response = self.client.delete("/bankcard/1/")
80+
81+
self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
82+
83+
@patch.object(BankCardViewSet, "partial_update")
84+
@patch.object(CognitoService, "login_user")
85+
def test_partial_update(self, mock_login_user, mock_partial_update):
86+
mock_login_user.return_value = {
87+
"AuthenticationResult": {"access_token": "mock_access_token", "id_token": "mock_id_token"}
88+
}
89+
update = {
90+
"account_number": 44467861,
91+
"bank_name": "Super Earth Bank",
92+
"card_type": "credit",
93+
}
94+
mock_partial_update.return_value = Response(status=status.HTTP_200_OK, data=update)
95+
96+
request = self.client.put("/bankcard/1/", {"bank_name": "Super Earth Bank"})
97+
request.headers["Authorization"] = "Bearer mock_access_token"
98+
99+
response = self.client.put("/bankcard/1/", {"bank_name": "Super Earth Bank"})
100+
101+
self.assertEqual(response.status_code, status.HTTP_200_OK)
102+
self.assertEqual(response.data["account_number"], 44467861)

bankcard/urls.py

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
from django.urls import path
2+
from .views import BankCardViewSet
3+
4+
urlpatterns = [
5+
path(
6+
"",
7+
BankCardViewSet.as_view({"get": "list", "post": "create"}),
8+
name="bankcard",
9+
),
10+
path(
11+
"<int:pk>/",
12+
BankCardViewSet.as_view({"get": "retrieve", "delete": "destroy", "put": "partial_update"}),
13+
name="individual",
14+
),
15+
]

bankcard/views.py

+99
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
from rest_framework import viewsets, status
2+
from rest_framework.response import Response
3+
4+
from authentication.decorators import cognito_authenticated
5+
from authentication.utils import get_user_id_from_token
6+
7+
from .models import BankCard
8+
from .serializers import BankCardSerializer
9+
10+
11+
class BankCardViewSet(viewsets.ViewSet):
12+
@cognito_authenticated
13+
def list(self, request):
14+
try:
15+
user_id = get_user_id_from_token(request)
16+
cards = BankCard.objects.filter(user_id=user_id)
17+
serializer = BankCardSerializer(cards, many=True)
18+
19+
if len(serializer.data) == 0:
20+
default = BankCardSerializer(
21+
data={"user_id": user_id, "account_number": 0, "bank_name": "None", "card_type": "default"}
22+
)
23+
24+
if default.is_valid():
25+
default.save()
26+
27+
return Response([default.data])
28+
29+
return Response(serializer.data)
30+
except BankCard.DoesNotExist:
31+
return Response({"error": "Bank cards not found"}, status=status.HTTP_404_NOT_FOUND)
32+
except Exception as e:
33+
return Response({"error": str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
34+
35+
@cognito_authenticated
36+
def create(self, request):
37+
try:
38+
user_id = get_user_id_from_token(request)
39+
data = request.data.copy()
40+
data["user_id"] = user_id
41+
42+
serializer = BankCardSerializer(data=data)
43+
44+
if serializer.is_valid():
45+
serializer.save()
46+
return Response(data=serializer.data, status=status.HTTP_201_CREATED)
47+
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
48+
except Exception as e:
49+
return Response({"error": str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
50+
51+
@cognito_authenticated
52+
def retrieve(self, request, pk=None):
53+
try:
54+
user_id = get_user_id_from_token(request)
55+
card = BankCard.objects.get(id=pk, user_id=user_id)
56+
serializer = BankCardSerializer(card)
57+
return Response(data=serializer.data)
58+
except BankCard.DoesNotExist:
59+
if pk == 1:
60+
default = BankCardSerializer(
61+
data={"user_id": user_id, "account_number": 0, "bank_name": "None", "card_type": "default"}
62+
)
63+
64+
if default.is_valid():
65+
default.save()
66+
67+
return Response(default.data)
68+
69+
return Response({"error": "Bank card not found"}, status=status.HTTP_404_NOT_FOUND)
70+
except Exception as e:
71+
return Response({"error": str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
72+
73+
@cognito_authenticated
74+
def destroy(self, request, pk=None):
75+
try:
76+
user_id = get_user_id_from_token(request)
77+
card = BankCard.objects.get(id=pk, user_id=user_id)
78+
card.delete()
79+
return Response(status=status.HTTP_204_NO_CONTENT)
80+
except BankCard.DoesNotExist:
81+
return Response({"error": "Bank card not found"}, status=status.HTTP_404_NOT_FOUND)
82+
except Exception as e:
83+
return Response({"error": str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
84+
85+
@cognito_authenticated
86+
def partial_update(self, request, pk=None):
87+
try:
88+
user_id = get_user_id_from_token(request)
89+
card = BankCard.objects.get(id=pk, user_id=user_id)
90+
serializer = BankCardSerializer(card, data=request.data, partial=True)
91+
92+
if serializer.is_valid():
93+
serializer.save()
94+
return Response(data=serializer.data)
95+
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
96+
except BankCard.DoesNotExist:
97+
return Response({"error": "Bank card not found"}, status=status.HTTP_404_NOT_FOUND)
98+
except Exception as e:
99+
return Response({"error": str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Generated by Django 5.0.6 on 2024-07-01 00:19
2+
3+
import django.db.models.deletion
4+
from django.conf import settings
5+
from django.db import migrations, models
6+
7+
8+
class Migration(migrations.Migration):
9+
10+
dependencies = [
11+
("bankcard", "0001_initial"),
12+
("expenses", "0001_initial"),
13+
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
14+
]
15+
16+
operations = [
17+
migrations.AlterField(
18+
model_name="expense",
19+
name="bankcard_id",
20+
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to="bankcard.bankcard"),
21+
),
22+
migrations.AlterField(
23+
model_name="expense",
24+
name="username",
25+
field=models.ForeignKey(
26+
on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, to_field="user_id"
27+
),
28+
),
29+
]

expenses/models.py

+5-3
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
from django.db import models
2+
from django.conf import settings
3+
24
from user_expense_type.models import UserExpenseType
35
from categories.models import Category
6+
from bankcard.models import BankCard
47

58

69
class Expense(models.Model):
710
id = models.AutoField(primary_key=True)
8-
username = models.UUIDField()
11+
username = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, to_field="user_id")
912
user_expense_type = models.ForeignKey(UserExpenseType, on_delete=models.CASCADE)
1013
category = models.ForeignKey(Category, on_delete=models.CASCADE)
11-
# cambiar cuando exista el modelo de bankcard
12-
bankcard_id = models.IntegerField()
14+
bankcard_id = models.ForeignKey(BankCard, on_delete=models.CASCADE)
1315
amount = models.IntegerField()
1416
created_at = models.DateTimeField(auto_now_add=True)
1517
updated_at = models.DateTimeField(auto_now=True)

expenses/serializers.py

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
11
from rest_framework import serializers
2+
from django.contrib.auth import get_user_model
23
from .models import Expense
34
from user_expense_type.models import UserExpenseType
45
from categories.models import Category
6+
from bankcard.models import BankCard
57

68

79
class ExpenseSerializer(serializers.Serializer):
810
id = serializers.IntegerField(read_only=True)
9-
10-
username = serializers.UUIDField()
11+
username = serializers.PrimaryKeyRelatedField(queryset=get_user_model().objects.all())
1112
user_expense_type = serializers.PrimaryKeyRelatedField(queryset=UserExpenseType.objects.all())
1213
category = serializers.PrimaryKeyRelatedField(queryset=Category.objects.all())
13-
bankcard_id = serializers.IntegerField()
14+
bankcard_id = serializers.PrimaryKeyRelatedField(queryset=BankCard.objects.all())
1415
amount = serializers.IntegerField()
1516

1617
def create(self, validated_data):

piggywallet/settings/base.py

+1
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
"categories",
5252
"expenses",
5353
"piggies",
54+
"bankcard",
5455
]
5556

5657
REST_FRAMEWORK = {

piggywallet/urls.py

+1
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,5 @@
2828
path("categories/", include("categories.urls")),
2929
path("expenses/", include("expenses.urls")),
3030
path("piggies/", include("piggies.urls")),
31+
path("bankcard/", include("bankcard.urls")),
3132
]

0 commit comments

Comments
 (0)