feat: transactions api done

Signed-off-by: Ameya Shenoy <shenoy.ameya@gmail.com>
This commit is contained in:
Ameya Shenoy 2023-03-22 16:48:37 +05:30
parent d7295d9f31
commit 1ae3e0ce2b
Signed by: codingcoffee
GPG key ID: EEC8EA855D61CEEC
9 changed files with 202 additions and 74 deletions

View file

@ -16,17 +16,49 @@ Including another URLconf
from django.conf import settings
from django.conf.urls.static import static
from django.contrib import admin
from django.urls import path
from drf_yasg.views import get_schema_view
from drf_yasg import openapi
from rest_framework import permissions
from django.urls import include, path, re_path
from foldbank.api import AccountsAPI, TransactionsAPI, UpcomingRecurringPaymentAPI
schema_view = get_schema_view(
openapi.Info(
title="Grapevine API",
default_version="v1",
description="",
terms_of_service="https://gvine.app/terms/",
contact=openapi.Contact(email="support@gvine.app"),
license=openapi.License(name="BSD License"),
),
public=True,
permission_classes=[permissions.AllowAny],
)
urlpatterns = [
path("admin/", admin.site.urls),
path("api/v1/transactions/", TransactionsAPI.as_view()),
path("api/v1/accounts/", AccountsAPI.as_view()),
path("api/v1/recurringPayments/upcoming/", UpcomingRecurringPaymentAPI.as_view()),
re_path(
r"^swagger(?P<format>\.json|\.yaml)$",
schema_view.without_ui(cache_timeout=0),
name="schema-json",
),
re_path(
r"^swagger/$",
schema_view.with_ui("swagger", cache_timeout=0),
name="schema-swagger-ui",
),
re_path(
r"^redoc/$",
schema_view.with_ui("redoc", cache_timeout=0),
name="schema-redoc",
),
]
if settings.DEBUG:
urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

View file

@ -84,6 +84,12 @@ class UserAdminConfig(UserAdmin):
)
class TransactionConfig(admin.ModelAdmin):
list_display = (
"from_account",
"created_at",
)
# Register your models here.
admin.site.register(User, UserAdminConfig)
@ -91,6 +97,6 @@ admin.site.register(Tag)
admin.site.register(Bank)
admin.site.register(Account)
admin.site.register(RecurringPayment)
admin.site.register(Transaction)
admin.site.register(Transaction, TransactionConfig)
admin.site.register(RecurringPaymentTransactionLink)

View file

@ -88,7 +88,7 @@ class LimitOffsetPagination(_LimitOffsetPagination):
def get_transactions(user):
return Transaction.objects.filter(from_account__user=user).order_by("created_at")
return Transaction.objects.filter(from_account__user=user).order_by("-created_at")
class TransactionsAPI(generics.ListAPIView):

View file

@ -1,5 +1,6 @@
# standard imports
# standard importspopulatedb
import datetime
from zoneinfo import ZoneInfo
# third-party imports
from django.utils.timezone import make_aware
@ -226,6 +227,9 @@ class Command(BaseCommand):
Account.objects.get_or_create(**account)
# recurring payment
ist = ZoneInfo("Asia/Kolkata")
today = datetime.datetime.now(tz=ist)
yesterday = datetime.datetime.now(tz=ist) - datetime.timedelta(days=1)
recurring_payments = [
{
"from_account": Account.objects.get(user=nishant, bank=Bank.objects.get(name="Axis Bank")),
@ -233,9 +237,9 @@ class Command(BaseCommand):
"amount": "36031",
"tag": Tag.objects.get(title="Rent"),
"defaults": {
"created_at": make_aware(datetime.datetime(year=2023, month=1, day=23)),
"created_at": datetime.datetime(year=2023, month=1, day=23, tzinfo=ist),
"frequency": datetime.timedelta(days=30),
"due_on": make_aware(datetime.datetime(year=2023, month=1, day=23)),
"due_on": datetime.datetime(year=2023, month=1, day=23, tzinfo=ist),
},
},
{
@ -244,9 +248,9 @@ class Command(BaseCommand):
"amount": "36031",
"tag": Tag.objects.get(title="Rent"),
"defaults": {
"created_at": make_aware(datetime.datetime(year=2023, month=1, day=20)),
"created_at": datetime.datetime(year=2023, month=1, day=20, tzinfo=ist),
"frequency": datetime.timedelta(days=30),
"due_on": make_aware(datetime.datetime(year=2023, month=2, day=19)),
"due_on": datetime.datetime(year=2023, month=2, day=19, tzinfo=ist),
}
},
]
@ -261,7 +265,7 @@ class Command(BaseCommand):
"amount": "3940",
"tag": Tag.objects.get(title="Tag"),
"defaults": {
"created_at": make_aware(datetime.datetime(year=2023, month=1, day=23, hour=11, minute=17)),
"created_at": datetime.datetime(year=today.year, month=today.month, day=today.day, hour=11, minute=17, tzinfo=ist),
}
},
{
@ -270,7 +274,7 @@ class Command(BaseCommand):
"amount": "35",
"tag": Tag.objects.get(title="Food & Drinks", sub_category="Drink"),
"defaults": {
"created_at": make_aware(datetime.datetime(year=2023, month=1, day=23, hour=23, minute=45)),
"created_at": datetime.datetime(year=today.year, month=today.month, day=today.day, hour=23, minute=45, tzinfo=ist),
}
},
{
@ -279,7 +283,7 @@ class Command(BaseCommand):
"amount": "2399",
"tag": Tag.objects.get(title="Groceries"),
"defaults": {
"created_at": make_aware(datetime.datetime(year=2023, month=1, day=23, hour=5, minute=37)),
"created_at": datetime.datetime(year=yesterday.year, month=yesterday.month, day=yesterday.day, hour=5, minute=37, tzinfo=ist),
}
},
{
@ -288,7 +292,7 @@ class Command(BaseCommand):
"amount": "312",
"tag": Tag.objects.get(title="Food & Drinks", sub_category="Food"),
"defaults": {
"created_at": make_aware(datetime.datetime(year=2023, month=1, day=23, hour=12, minute=17)),
"created_at": datetime.datetime(year=yesterday.year, month=yesterday.month, day=yesterday.day, hour=12, minute=17, tzinfo=ist),
}
},
{
@ -297,7 +301,7 @@ class Command(BaseCommand):
"amount": "75",
"tag": Tag.objects.get(title="Transport"),
"defaults": {
"created_at": make_aware(datetime.datetime(year=2023, month=1, day=2, hour=10, minute=32)),
"created_at": datetime.datetime(year=2023, month=1, day=2, hour=10, minute=32, tzinfo=ist),
}
},
{
@ -306,7 +310,7 @@ class Command(BaseCommand):
"amount": "249",
"tag": Tag.objects.get(title="Food & Drinks", sub_category="Swiggy"),
"defaults": {
"created_at": make_aware(datetime.datetime(year=2023, month=1, day=1, hour=11, minute=17)),
"created_at": datetime.datetime(year=2023, month=1, day=1, hour=11, minute=17, tzinfo=ist),
}
},
{
@ -315,11 +319,13 @@ class Command(BaseCommand):
"amount": "199",
"tag": Tag.objects.get(title="Subscription", sub_category="Netflix"),
"defaults": {
"created_at": make_aware(datetime.datetime(year=2023, month=1, day=23, hour=23, minute=45)),
"created_at": datetime.datetime(year=2022, month=12, day=31, hour=23, minute=45, tzinfo=ist),
}
},
]
# Transaction.objects.all().delete()
for t in transactions:
Transaction.objects.get_or_create(**t)
tnx, _ = Transaction.objects.get_or_create(**t)
tnx.created_at = t["defaults"]["created_at"]
tnx.save()

View file

@ -1,8 +1,9 @@
# Django
SECRET_KEY=sample
DEBUG=true
ALLOWED_HOSTS=0.0.0.0,localhost,127.0.0.1,*
CSRF_TRUSTED_ORIGINS=https://foldbank.codingcoffee.me
ALLOWED_HOSTS=0.0.0.0,localhost,127.0.0.1,localhost:3000
CSRF_TRUSTED_ORIGINS=http://localhost:3000,https://foldbank.codingcoffee.me
CORS_ALLOWED_ORIGINS=http://localhost:3000,https://foldbank.codingcoffee.me
DJANGO_LOG_LEVEL=INFO
# Django - Postgres
POSTGRES_SERVER=postgres-db

View file

@ -3,15 +3,15 @@
version: '3.7'
services:
# web:
# image: node:16.19.1-alpine3.17
# restart: unless-stopped
# volumes:
# - ./web:/app
# working_dir: /app
# entrypoint: npm run dev
# ports:
# - 3000:3000
web:
image: node:16.19.1-alpine3.17
restart: unless-stopped
volumes:
- ./web:/app
working_dir: /app
entrypoint: npm run dev
ports:
- 3000:3000
backend:
image: foldbank-backend-dev

View file

@ -1,64 +1,109 @@
"use client"
import React from 'react'
import styles from './recentTnx.module.css'
import { BsTriangleFill } from 'react-icons/bs';
import SingleTransaction from './SingleTransaction'
import useSWR from 'swr'
export default function RecentTransactions() {
const transactions = [
{
title: "Fenny's Banglore",
datetime: "Today, 11:17 am",
amount: "3,940",
tag: "Tag",
}, {
title: "Sendoor",
datetime: "Today, 11:45 pm",
amount: "35",
tag: "Food & Drinks",
icon_type: "drink",
}, {
title: "Reliance Fresh",
datetime: "Yesterday, 5:37 pm",
amount: "2,399",
tag: "Groceries",
icon_type: "groceries",
}, {
title: "Chai Point",
datetime: "Yesterday, 12:17 pm",
amount: "312",
tag: "Food & Drinks",
icon_type: "food",
}, {
title: "Uber",
datetime: "Jan 2, 10:32 am",
amount: "75",
tag: "Transport",
icon_type: "transport",
}, {
title: "Swiggy",
datetime: "Jan 1, 11:17 pm",
amount: "249",
tag: "Food & Drinks",
icon_type: "swiggy",
}, {
title: "Netflix",
datetime: "Dec 31, 11:59 pm",
amount: "199",
tag: "Subsciption",
icon_type: "netflix",
const fetcher = (...args) => fetch(...args).then((res) => res.json())
const { data, error } = useSWR(`http://localhost:8000/api/v1/transactions/`, fetcher)
if (error) return <div>Error: Failed to load</div>
if (!data) return <div>Loading...</div>
const transactions = data.results
// const transactions = [
// {
// title: "Fenny's Banglore",
// datetime: "Today, 11:17 am",
// amount: "3,940",
// tag: "Tag",
// }, {
// title: "Sendoor",
// datetime: "Today, 11:45 pm",
// amount: "35",
// tag: "Food & Drinks",
// icon_type: "drink",
// }, {
// title: "Reliance Fresh",
// datetime: "Yesterday, 5:37 pm",
// amount: "2,399",
// tag: "Groceries",
// icon_type: "groceries",
// }, {
// title: "Chai Point",
// datetime: "Yesterday, 12:17 pm",
// amount: "312",
// tag: "Food & Drinks",
// icon_type: "food",
// }, {
// title: "Uber",
// datetime: "Jan 2, 10:32 am",
// amount: "75",
// tag: "Transport",
// icon_type: "transport",
// }, {
// title: "Swiggy",
// datetime: "Jan 1, 11:17 pm",
// amount: "249",
// tag: "Food & Drinks",
// icon_type: "swiggy",
// }, {
// title: "Netflix",
// datetime: "Dec 31, 11:59 pm",
// amount: "199",
// tag: "Subsciption",
// icon_type: "netflix",
// }
// ]
const relateive_date = (date) => {
const today = new Date();
let fuzzy;
if (today.getYear() == date.getYear()) {
if (today.getMonth() == date.getMonth()) {
if (today.getDate() == date.getDate()) {
fuzzy = 'Today';
} else if (today.getDate() == date.getDate() + 1) {
fuzzy = 'Yesterday';
}
]
}
}
if (fuzzy == undefined){
const monthNames = [
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
];
fuzzy = `${monthNames[date.getMonth()]} ${date.getDate()}`
}
let hours = date.getHours()
let ampm = 'AM';
if (hours >= 12) {
ampm = 'PM';
hours -= 12;
}
fuzzy = fuzzy + `, ${hours}:${date.getMinutes()} ${ampm}`
return fuzzy
}
const recent_transactions = [];
transactions.forEach((transaction) => {
recent_transactions.push(
<SingleTransaction
title={transaction.title}
tag={transaction.tag}
datetime={transaction.datetime}
title={transaction.to_account.holders_name}
tag={transaction.tag.title}
datetime={relateive_date(new Date(transaction.created_at))}
amount={transaction.amount}
icon_type={transaction.icon_type}
icon_type={transaction.tag.icon_type}
/>
)
})

37
web/package-lock.json generated
View file

@ -23,6 +23,7 @@
"react": "18.2.0",
"react-dom": "18.2.0",
"react-icons": "^4.7.1",
"swr": "^2.1.0",
"typescript": "4.9.5"
}
},
@ -3901,6 +3902,20 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/swr": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/swr/-/swr-2.1.0.tgz",
"integrity": "sha512-4hYl5p3/KalQ1MORealM79g/DtLohmud6yyfXw5l4wjtFksYUnocRFudvyaoUtgj3FrVNK9lS25Av9dsZYvz0g==",
"dependencies": {
"use-sync-external-store": "^1.2.0"
},
"engines": {
"pnpm": "7"
},
"peerDependencies": {
"react": "^16.11.0 || ^17.0.0 || ^18.0.0"
}
},
"node_modules/synckit": {
"version": "0.8.5",
"resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.5.tgz",
@ -4061,6 +4076,14 @@
"punycode": "^2.1.0"
}
},
"node_modules/use-sync-external-store": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz",
"integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==",
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
}
},
"node_modules/which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
@ -6776,6 +6799,14 @@
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="
},
"swr": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/swr/-/swr-2.1.0.tgz",
"integrity": "sha512-4hYl5p3/KalQ1MORealM79g/DtLohmud6yyfXw5l4wjtFksYUnocRFudvyaoUtgj3FrVNK9lS25Av9dsZYvz0g==",
"requires": {
"use-sync-external-store": "^1.2.0"
}
},
"synckit": {
"version": "0.8.5",
"resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.5.tgz",
@ -6895,6 +6926,12 @@
"punycode": "^2.1.0"
}
},
"use-sync-external-store": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz",
"integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==",
"requires": {}
},
"which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",

View file

@ -24,6 +24,7 @@
"react": "18.2.0",
"react-dom": "18.2.0",
"react-icons": "^4.7.1",
"swr": "^2.1.0",
"typescript": "4.9.5"
}
}