From 8702233934ea71e53a847b1229672e07d8275333 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20M=C3=BCller=20=28ChaoticByte=29?= Date: Fri, 14 Apr 2023 23:47:12 +0200 Subject: [PATCH] Added feature to transfer money to another account + some minor improvements --- app/db_queries.py | 15 ++++- app/locales/de/LC_MESSAGES/django.mo | Bin 3513 -> 3704 bytes app/locales/de/LC_MESSAGES/django.po | 40 ++++++++---- .../0004_registertransaction_is_transfer.py | 17 +++++ app/models.py | 13 ++-- app/static/css/main.css | 2 +- app/templates/registration/login.html | 2 + app/templates/transfer.html | 60 ++++++++++++++++++ app/templates/userpanel.html | 1 + app/urls.py | 2 + app/views.py | 42 +++++++++--- 11 files changed, 164 insertions(+), 30 deletions(-) create mode 100644 app/migrations/0004_registertransaction_is_transfer.py create mode 100644 app/templates/transfer.html diff --git a/app/db_queries.py b/app/db_queries.py index 93a2727..c10b85e 100644 --- a/app/db_queries.py +++ b/app/db_queries.py @@ -45,21 +45,30 @@ def select_history(user, language_code="en") -> list: result = _db_select(f""" select concat( + price_sum, '{settings.CURRENCY_SUFFIX} - ', product_name, ' (', content_litres::real, -- converting to real removes trailing zeros - 'l) x ', amount, ' - ', price_sum, '{settings.CURRENCY_SUFFIX}') as "text", + 'l) x ', amount) as "text", datetime from app_order where user_id = {user_id} - union + union select - concat('Deposit: +', transaction_sum, '{settings.CURRENCY_SUFFIX}') as "text", + concat(transaction_sum, '{settings.CURRENCY_SUFFIX} - Deposit') as "text", datetime from app_userdeposits_view where user_id = {user_id} + union + + select + concat(transaction_sum, '{settings.CURRENCY_SUFFIX} - ', comment) as "text", + datetime + from app_registertransaction + where user_id = {user_id} and is_transfer = true + order by datetime desc fetch first 30 rows only; """) diff --git a/app/locales/de/LC_MESSAGES/django.mo b/app/locales/de/LC_MESSAGES/django.mo index 9bbfd05f0edcc8ef85efb156f920675466a7fec2..9ad7ad461d4d79a5ce679d7d73db94e66efd4f59 100644 GIT binary patch delta 1490 zcmYk+TS!zv9LMol-Mp+@rj?g8+cM2d-b(Y9l%N@6MOGFRh2*i!_9Ux85w%3wQb~2UjQ4N}K0)388u^)NF1l|9 zlZ*+QSVmn&Lo%jd9_q$w)W8ka9k`JCVbs7uT!a^_L$-YcwSWj}yfM^-?{NiAB0n?3 zrI`6m483Y7#idwX}%GTTl-kM-Awr7f+)GI){v5E}`zbf?CKBDgw7{{h{@-^%?5< z7Z}!quPEet2o-9;DQ43^zpxIcQEyVtMO#&m>feQm%s$(G3|pz6M@=w+TKHR3q&}ja zpF&0YuaEdMr%5H=>c~RvZ3SxJW=z8l+aI)E#B|#4SVyfBs2BKxdH5YQeloMyVg{<- zjzxIDPy90|T&96VnGwvyM_7pCxEy~XKa2d3Hz*p>q=B?8`O$`XmQ&tHE+K0Rw2eAL ztI7HttI4Hg-dA5s*kn=AAB5h1e$wg{0VSOcy`>_fZBPXCEw3Thkd=zavwMyDOci#e zoH=H%ibNGzhc7@bGn?nas>2aY+V7Ro3a+)~V$|8tb}8kOQzPHJS3N0Bp&qxlKTs0b z6Y6yPyPeQ*DDHP+yK|(cug7r*hCAac)1zG`S_o`E={jd3H{(ZR(_5T@(AWdF*I|qn o=R{Ya-*LMfH`0`})8lIn_I1;Flwoi9+qn0CznNDap7ekE2Sy)^asU7T delta 1315 zcmZA1%}Z2K7{~Ev9A749Mr+E-d@C&*o5nJ=7h9wY70MS#uw8<86)}aGkiBrDKcLOU zqD9zdRv8roMi5B*YLU1Kgg{D#h`@!psJ_4Pxan}u{hWKa_uS_`&zV`Tep}@)CJLvF zc7|R}UkaHG;!KzmZMo2_1Xr*af8hyS$8DG@GTV;rsQY@b90zbGUPj%2134_qN%uX+ zsF`m$1{F-q;SO9t4*SkY3;%Hb!8q6DM4^Riunb$Aoo;>*6@Z6&(Ky!N9ptbloKpBK zf9;!nV6cmcc_boRMg_2j3Sa{dV3gGon8p}(qVDTQ1u}%CIEqSM26@!RP?^1jjW~r( z_zuIo-@Y+WDp!%L+8XM?-^gJW*<3J!dSMLpg$Z|E?@T(IQO~!b0y&Efl=U2HJ&%`Z zovY|~Fc@Q?$Y)Tcc;jyPh|0jcn_t57TyLOW(9J^(tq+xvVbt>;Dx;IgJME#He~c>K z3)H&rOR4{E2A|yxE6%^ThxrQXA=9YHyYUeAqZS@VZQQ%=dKQmxJ%@X76qWh=4;AnwRE9=S z>s>*WWCHa8lRg8bX%r6vq&lP8w#v}o}?=adZDtV-Tr@TW}=?1 zAk?&>^|SO3b7IYWQ|+{qRhzA*;Ogi~t4gJ&0M(RAwd0#uJD#K~gZfHkNKL74%#X~f zocgH!`u!_iYO2>()0ZBkx3~$VN=+%&#yU+`8MGIU(e=Nfdg@o9mZmFvRL5%RDZ0vZ uif&z=cYWg8P3y`)uBYf;C|D@|8GI^f%~eKTg>zq{1EHWJej#`o|MCyDt7lvQ diff --git a/app/locales/de/LC_MESSAGES/django.po b/app/locales/de/LC_MESSAGES/django.po index 23307a8..855cd09 100644 --- a/app/locales/de/LC_MESSAGES/django.po +++ b/app/locales/de/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-04-13 20:50+0200\n" +"POT-Creation-Date: 2023-04-14 23:37+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Julian Müller (ChaoticByte)\n" "Language: DE\n" @@ -33,20 +33,21 @@ msgstr "Ein Fehler ist aufgetreten. Bitte ab- und wieder anmelden." msgid "Drinks - Deposit" msgstr "Getränke - Einzahlen" -#: app/templates/deposit.html:17 app/templates/userpanel.html:23 +#: app/templates/deposit.html:17 app/templates/userpanel.html:18 msgid "Deposit" msgstr "Einzahlen" -#: app/templates/deposit.html:19 +#: app/templates/deposit.html:19 app/templates/transfer.html:43 msgid "Amount" msgstr "Summe" #: app/templates/deposit.html:30 app/templates/order.html:54 #: app/templates/registration/login.html:28 app/templates/supply.html:29 +#: app/templates/transfer.html:54 msgid "cancel" msgstr "Abbrechen" -#: app/templates/deposit.html:31 +#: app/templates/deposit.html:31 app/templates/transfer.html:55 msgid "confirm" msgstr "Bestätigen" @@ -54,7 +55,7 @@ msgstr "Bestätigen" msgid "Drinks - History" msgstr "Getränke - Verlauf" -#: app/templates/history.html:10 app/templates/userpanel.html:30 +#: app/templates/history.html:10 app/templates/userpanel.html:25 msgid "History" msgstr "Verlauf" @@ -164,7 +165,7 @@ msgstr "Wähle deinen Account" msgid "Drinks - Statistics" msgstr "Getränke - Statistiken" -#: app/templates/statistics.html:10 app/templates/userpanel.html:31 +#: app/templates/statistics.html:10 app/templates/userpanel.html:26 msgid "Statistics" msgstr "Statistiken" @@ -206,7 +207,7 @@ msgstr "Tag" msgid "Drinks - Supply" msgstr "Getränke - Beschaffung" -#: app/templates/supply.html:14 app/templates/userpanel.html:36 +#: app/templates/supply.html:14 app/templates/userpanel.html:32 msgid "Supply" msgstr "Beschaffung" @@ -226,19 +227,36 @@ msgstr "Senden" msgid "You are not allowed to view this site." msgstr "Dir fehlt die Berechtigung, diese Seite anzuzeigen." -#: app/templates/userpanel.html:15 app/templates/userpanel.html:17 +#: app/templates/transfer.html:6 +#| msgid "Drinks - Order" +msgid "Drinks - Transfer" +msgstr "Getränke - Geld senden" + +#: app/templates/transfer.html:17 +msgid "Transfer Money" +msgstr "Geld senden" + +#: app/templates/transfer.html:19 +msgid "Recipient" +msgstr "Empfänger" + +#: app/templates/userpanel.html:10 app/templates/userpanel.html:12 msgid "Balance" msgstr "Saldo" -#: app/templates/userpanel.html:24 +#: app/templates/userpanel.html:19 msgid "Logout" msgstr "Abmelden" -#: app/templates/userpanel.html:27 +#: app/templates/userpanel.html:22 msgid "Account" msgstr "Account" -#: app/templates/userpanel.html:38 +#: app/templates/userpanel.html:30 +msgid "Transfer" +msgstr "Geld senden" + +#: app/templates/userpanel.html:34 msgid "Change Password" msgstr "Passwort ändern" diff --git a/app/migrations/0004_registertransaction_is_transfer.py b/app/migrations/0004_registertransaction_is_transfer.py new file mode 100644 index 0000000..550b353 --- /dev/null +++ b/app/migrations/0004_registertransaction_is_transfer.py @@ -0,0 +1,17 @@ +# Generated by Django 4.1.6 on 2023-04-14 20:01 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("app", "0003_user_hide_from_userlist"), + ] + + operations = [ + migrations.AddField( + model_name="registertransaction", + name="is_transfer", + field=models.BooleanField(default=False), + ), + ] diff --git a/app/models.py b/app/models.py index 5978a47..842f889 100644 --- a/app/models.py +++ b/app/models.py @@ -59,30 +59,29 @@ class RegisterTransaction(models.Model): old_transaction_sum = models.DecimalField(max_digits=6, decimal_places=2, default=0.00) datetime = models.DateTimeField(default=timezone.now) is_user_deposit = models.BooleanField(default=False) + is_transfer = models.BooleanField(default=False) comment = models.TextField(default=" ") user = models.ForeignKey(User, on_delete=models.CASCADE) def save(self, *args, **kwargs): if self._state.adding: - if self.is_user_deposit == True: # update user balance + if self.is_user_deposit or self.is_transfer: # update user balance self.user.balance += self.transaction_sum self.user.save() self.old_transaction_sum = self.transaction_sum super().save(*args, **kwargs) else: - # update register transaction - sum_diff = self.transaction_sum - self.old_transaction_sum # update user balance - if self.is_user_deposit == True: - ub_sum_diff = self.transaction_sum - self.old_transaction_sum - self.user.balance += ub_sum_diff + if self.is_user_deposit or self.is_transfer: + self.user.balance += self.transaction_sum - self.old_transaction_sum self.user.save() + # update register transaction self.old_transaction_sum = self.transaction_sum super().save(*args, **kwargs) def delete(self, *args, **kwargs): # update user deposit - if self.is_user_deposit: + if self.is_user_deposit or self.is_transfer: self.user.balance -= self.transaction_sum self.user.save() super().delete(*args, kwargs) diff --git a/app/static/css/main.css b/app/static/css/main.css index afdd6c6..338fb35 100644 --- a/app/static/css/main.css +++ b/app/static/css/main.css @@ -50,7 +50,7 @@ input[type="number"]::-webkit-inner-spin-button { display: none; } -input[type="text"], input[type="password"], input[type="number"] { +input[type="text"], input[type="password"], input[type="number"], select { padding: .6rem .8rem; text-align: center; font-size: 1rem; diff --git a/app/templates/registration/login.html b/app/templates/registration/login.html index 837dc70..c9ba292 100644 --- a/app/templates/registration/login.html +++ b/app/templates/registration/login.html @@ -48,6 +48,8 @@ {{ user_.last_name }}, {% endif %} {{ user_.first_name }} + {% elif user_.last_name %} + {{ user_.last_name }} {% else %} {{ user_.username }} {% endif %} diff --git a/app/templates/transfer.html b/app/templates/transfer.html new file mode 100644 index 0000000..74c13f4 --- /dev/null +++ b/app/templates/transfer.html @@ -0,0 +1,60 @@ +{% extends "baselayout.html" %} + +{% load i18n %} + +{% block title %} +{% translate "Drinks - Transfer" %} +{% endblock %} + +{% block headAdditional %} + + +{% endblock %} + +{% block content %} +
+ {% csrf_token %} +

{% translate "Transfer Money" %}

+
+ {% translate "Recipient" %}: + + + +
+
+ {% translate "Amount" %} {{ currency_suffix }}: + + + +
+
+ +
+ + + +
+ + +{% endblock %} \ No newline at end of file diff --git a/app/templates/userpanel.html b/app/templates/userpanel.html index baa384b..4423150 100644 --- a/app/templates/userpanel.html +++ b/app/templates/userpanel.html @@ -27,6 +27,7 @@ {% if user.is_superuser or user.is_staff %} Admin Panel {% endif %} + {% translate "Transfer" %} {% if user.is_superuser or user.allowed_to_supply %} {% translate "Supply" %} {% endif %} diff --git a/app/urls.py b/app/urls.py index f6f8cc4..1fa379d 100644 --- a/app/urls.py +++ b/app/urls.py @@ -10,6 +10,7 @@ urlpatterns = [ path('history/', views.history), path('deposit/', views.deposit), path('statistics/', views.statistics), + path('transfer/', views.transfer), path('supply/', views.supply), path('accounts/login/', views.login_page, name="login"), path('accounts/logout/', auth_views.LogoutView.as_view(), name='logout'), @@ -19,5 +20,6 @@ urlpatterns = [ # API # path('api/order-drink', views.api_order_drink), path('api/deposit', views.api_deposit), + path('api/transfer', views.api_transfer), path('api/supply', views.api_supply) ] diff --git a/app/views.py b/app/views.py index 07fd072..d604e63 100644 --- a/app/views.py +++ b/app/views.py @@ -50,7 +50,6 @@ def login_page(request): "user_list": userlist }) - @login_required def index(request): context = { @@ -58,7 +57,6 @@ def index(request): } return render(request, "index.html", context) - @login_required def history(request): context = { @@ -66,7 +64,6 @@ def history(request): } return render(request, "history.html", context) - @login_required def order(request, drinkid): try: @@ -76,7 +73,6 @@ def order(request, drinkid): except Drink.DoesNotExist: return HttpResponseRedirect("/") - @login_required def deposit(request): return render(request, "deposit.html", {}) @@ -91,17 +87,19 @@ def statistics(request): } return render(request, "statistics.html", context) +@login_required +def transfer(request): + userlist = get_user_model().objects.filter(hide_from_userlist=False).filter(is_active=True).order_by("username") + return render(request, "transfer.html", {"user_list": userlist}) @login_required def supply(request): return render(request, "supply.html") - @login_required def redirect_home(request): return HttpResponseRedirect("/") - # API for XHR requests # @login_required @@ -123,7 +121,6 @@ def api_order_drink(request): print(f"An exception occured while processing an order: User: {user.username} - Exception: {e}", file=sys.stderr) return HttpResponse(b"", status=500) - @login_required def api_deposit(request): # check request -> deposit @@ -140,9 +137,38 @@ def api_deposit(request): return HttpResponse("success", status=200) else: raise Exception("Deposit amount too big or small.") except Exception as e: - print(f"An exception occured while processing a transaction: User: {user.username} - Exception: {e}", file=sys.stderr) + print(f"An exception occured while processing a deposit transaction: User: {user.username} - Exception: {e}", file=sys.stderr) return HttpResponse(b"", status=500) +@login_required +def api_transfer(request): + # check request -> transfer + user = request.user + try: + recipient = get_user_model().objects.get(id=int(request.POST["recipientuser"])) + if recipient.id == user.id: + raise Exception(f"User {user.username} tried to transfer to themself.") + amount = decimal.Decimal(request.POST["transferamount"]) + if 0.00 < amount <= user.balance: + print("sender:", user.username) + print("recipient:", recipient.username) + print("amount:", amount) + # create transaction + RegisterTransaction.objects.create( + transaction_sum=-amount, + comment=f"Transfer to {recipient.username}", + is_transfer=True, + user=user) + RegisterTransaction.objects.create( + transaction_sum=amount, + comment=f"Transfer from {user.username}", + is_transfer=True, + user=recipient) + return HttpResponse("success", status=200) + else: raise Exception("Transfer amount too big or small.") + except Exception as e: + print(f"An exception occured while processing a transfer transaction: User: {user.username} - Exception: {e}", file=sys.stderr) + return HttpResponse(b"", status=500) @login_required def api_supply(request):