Skip to content

Commit 72569f0

Browse files
Merge pull request #18 from Kalgoc/gastos
Gastos
2 parents 5c7c3f7 + 32eec99 commit 72569f0

File tree

12 files changed

+426
-0
lines changed

12 files changed

+426
-0
lines changed

expenses/__init__.py

Whitespace-only changes.

expenses/admin.py

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
from django.contrib import admin
2+
from .models import Expense
3+
4+
5+
@admin.register(Expense)
6+
class ExpenseAdmin(admin.ModelAdmin):
7+
list_display = (
8+
"username",
9+
"amount",
10+
"user_expense_type_name",
11+
"category_name",
12+
"bankcard_id",
13+
"created_at",
14+
"updated_at",
15+
)
16+
17+
def user_expense_type_name(self, obj):
18+
return obj.user_expense_type.name
19+
20+
user_expense_type_name.short_description = "User Expense Type"
21+
22+
def category_name(self, obj):
23+
return obj.category.name
24+
25+
category_name.short_description = "Category"

expenses/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 ExpensesConfig(AppConfig):
5+
default_auto_field = "django.db.models.BigAutoField"
6+
name = "expenses"

expenses/migrations/0001_initial.py

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# Generated by Django 5.0.6 on 2024-06-17 13:41
2+
3+
import django.db.models.deletion
4+
from django.db import migrations, models
5+
6+
7+
class Migration(migrations.Migration):
8+
9+
initial = True
10+
11+
dependencies = [
12+
("categories", "0001_initial"),
13+
("user_expense_type", "0001_initial"),
14+
]
15+
16+
operations = [
17+
migrations.CreateModel(
18+
name="Expense",
19+
fields=[
20+
("id", models.AutoField(primary_key=True, serialize=False)),
21+
("username", models.UUIDField()),
22+
("bankcard_id", models.IntegerField()),
23+
("amount", models.IntegerField()),
24+
("created_at", models.DateTimeField(auto_now_add=True)),
25+
("updated_at", models.DateTimeField(auto_now=True)),
26+
("category", models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to="categories.category")),
27+
(
28+
"user_expense_type",
29+
models.ForeignKey(
30+
on_delete=django.db.models.deletion.CASCADE, to="user_expense_type.userexpensetype"
31+
),
32+
),
33+
],
34+
),
35+
]

expenses/migrations/__init__.py

Whitespace-only changes.

expenses/models.py

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
from django.db import models
2+
from user_expense_type.models import UserExpenseType
3+
from categories.models import Category
4+
5+
6+
class Expense(models.Model):
7+
id = models.AutoField(primary_key=True)
8+
username = models.UUIDField()
9+
user_expense_type = models.ForeignKey(UserExpenseType, on_delete=models.CASCADE)
10+
category = models.ForeignKey(Category, on_delete=models.CASCADE)
11+
# cambiar cuando exista el modelo de bankcard
12+
bankcard_id = models.IntegerField()
13+
amount = models.IntegerField()
14+
created_at = models.DateTimeField(auto_now_add=True)
15+
updated_at = models.DateTimeField(auto_now=True)

expenses/serializers.py

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
from rest_framework import serializers
2+
from .models import Expense
3+
from user_expense_type.models import UserExpenseType
4+
from categories.models import Category
5+
6+
7+
class ExpenseSerializer(serializers.Serializer):
8+
id = serializers.IntegerField(read_only=True)
9+
10+
username = serializers.UUIDField()
11+
user_expense_type = serializers.PrimaryKeyRelatedField(queryset=UserExpenseType.objects.all())
12+
category = serializers.PrimaryKeyRelatedField(queryset=Category.objects.all())
13+
bankcard_id = serializers.IntegerField()
14+
amount = serializers.IntegerField()
15+
16+
def create(self, validated_data):
17+
return Expense.objects.create(**validated_data)
18+
19+
def update(self, instance, validated_data):
20+
instance.amount = validated_data.get("amount", instance.amount)
21+
instance.save()
22+
return instance

expenses/tests.py

+135
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
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 ExpenseViewSet, ExpenseGroupedByTypeAndCategoryViewSet
8+
9+
10+
class ExpenseViewSetTestCase(TestCase):
11+
def setUp(self):
12+
self.client = APIClient()
13+
self.view = ExpenseViewSet()
14+
self.expense = {
15+
"user_expense_type": 1,
16+
"category": 1,
17+
"bankcard_id": 1,
18+
"amount": 100,
19+
}
20+
21+
@patch.object(ExpenseViewSet, "create")
22+
@patch.object(CognitoService, "login_user")
23+
def test_create(self, mock_login_user, mock_create):
24+
mock_login_user.return_value = {
25+
"AuthenticationResult": {"AccessToken": "mock_access_token", "IdToken": "mock_id_token"}
26+
}
27+
mock_create.return_value = Response(status=status.HTTP_201_CREATED, data=self.expense)
28+
29+
request = self.client.post("/expenses/", self.expense)
30+
request.headers["Authorization"] = "Bearer mock_access_token"
31+
32+
response = self.client.post("/expenses/", self.expense)
33+
34+
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
35+
self.assertEqual(response.data["amount"], 100)
36+
37+
@patch.object(ExpenseViewSet, "list")
38+
@patch.object(CognitoService, "login_user")
39+
def test_list(self, mock_login_user, mock_list):
40+
mock_login_user.return_value = {
41+
"AuthenticationResult": {"AccessToken": "mock_access_token", "IdToken": "mock_id_token"}
42+
}
43+
mock_list.return_value = Response(status=status.HTTP_200_OK, data=self.expense)
44+
45+
request = self.client.get("/expenses/")
46+
request.headers["Authorization"] = "Bearer mock_access_token"
47+
48+
response = self.client.get("/expenses/")
49+
50+
self.assertEqual(response.status_code, status.HTTP_200_OK)
51+
self.assertEqual(response.data["amount"], 100)
52+
53+
@patch.object(ExpenseViewSet, "retrieve")
54+
@patch.object(CognitoService, "login_user")
55+
def test_retrieve(self, mock_login_user, mock_retrieve):
56+
mock_login_user.return_value = {
57+
"AuthenticationResult": {"AccessToken": "mock_access_token", "IdToken": "mock_id_token"}
58+
}
59+
mock_retrieve.return_value = Response(status=status.HTTP_200_OK, data=self.expense)
60+
61+
request = self.client.get("/expenses/1/")
62+
request.headers["Authorization"] = "Bearer mock_access_token"
63+
64+
response = self.client.get("/expenses/1/")
65+
66+
self.assertEqual(response.status_code, status.HTTP_200_OK)
67+
self.assertEqual(response.data["amount"], 100)
68+
69+
@patch.object(ExpenseViewSet, "destroy")
70+
@patch.object(CognitoService, "login_user")
71+
def test_destroy(self, mock_login_user, mock_destroy):
72+
mock_login_user.return_value = {
73+
"AuthenticationResult": {"AccessToken": "mock_access_token", "IdToken": "mock_id_token"}
74+
}
75+
mock_destroy.return_value = Response(status=status.HTTP_204_NO_CONTENT)
76+
77+
request = self.client.delete("/expenses/1/")
78+
request.headers["Authorization"] = "Bearer mock_access_token"
79+
80+
response = self.client.delete("/expenses/1/")
81+
82+
self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
83+
84+
@patch.object(ExpenseViewSet, "partial_update")
85+
@patch.object(CognitoService, "login_user")
86+
def test_partial_update(self, mock_login_user, mock_partial_update):
87+
mock_login_user.return_value = {
88+
"AuthenticationResult": {"AccessToken": "mock_access_token", "IdToken": "mock_id_token"}
89+
}
90+
new_expense = {
91+
"expense_type": 1,
92+
"category": 1,
93+
"bankcard_id": 1,
94+
"amount": 200,
95+
}
96+
mock_partial_update.return_value = Response(status=status.HTTP_200_OK, data=new_expense)
97+
98+
request = self.client.put("/expenses/1/", {"amount": 200})
99+
request.headers["Authorization"] = "Bearer mock_access_token"
100+
101+
response = self.client.put("/expenses/1/", {"amount": 200})
102+
103+
self.assertEqual(response.status_code, status.HTTP_200_OK)
104+
self.assertEqual(response.data["amount"], 200)
105+
106+
107+
class ExpenseGroupedByTypeAndCategoryViewSetTestCase(TestCase):
108+
def setUp(self):
109+
self.client = APIClient()
110+
self.view = ExpenseGroupedByTypeAndCategoryViewSet()
111+
self.expense = {
112+
"expense_type": 1,
113+
"category": 1,
114+
"bankcard_id": 1,
115+
"amount": 100,
116+
}
117+
self.expenses_grouped = {"Personal": {"Comida": 100}}
118+
119+
@patch.object(ExpenseGroupedByTypeAndCategoryViewSet, "list")
120+
@patch.object(CognitoService, "login_user")
121+
def test_list(self, mock_login_user, mock_list):
122+
mock_login_user.return_value = {
123+
"AuthenticationResult": {"AccessToken": "mock_access_token", "IdToken": "mock_id_token"}
124+
}
125+
mock_list.return_value = Response(status=status.HTTP_200_OK, data=self.expenses_grouped)
126+
127+
request = self.client.get("/expenses/grouped/")
128+
request.headers["Authorization"] = "Bearer mock_access_token"
129+
130+
response = self.client.get("/expenses/grouped/")
131+
132+
self.assertEqual(response.status_code, status.HTTP_200_OK)
133+
self.assertIn("Personal", response.data)
134+
self.assertIn("Comida", response.data["Personal"])
135+
self.assertEqual(response.data["Personal"]["Comida"], 100)

expenses/urls.py

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
from django.urls import path
2+
from .views import ExpenseViewSet, ExpenseGroupedByTypeAndCategoryViewSet
3+
4+
urlpatterns = [
5+
path(
6+
"",
7+
ExpenseViewSet.as_view({"get": "list", "post": "create"}),
8+
name="expenses",
9+
),
10+
path(
11+
"<int:pk>/",
12+
ExpenseViewSet.as_view({"get": "retrieve", "put": "partial_update", "delete": "destroy"}),
13+
name="expense-detail",
14+
),
15+
path(
16+
"grouped/",
17+
ExpenseGroupedByTypeAndCategoryViewSet.as_view({"get": "list"}),
18+
name="expenses_grouped",
19+
),
20+
]

0 commit comments

Comments
 (0)