From e68709092b2b7297135ee141423cac136afc6d30 Mon Sep 17 00:00:00 2001 From: Dan Cojocaru Date: Mon, 3 Jan 2022 21:42:03 +0200 Subject: [PATCH 1/3] Fixed datetime not having correct timezone --- server/foxbank_server/apis/notifications.py | 4 ++-- server/foxbank_server/apis/transactions.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/server/foxbank_server/apis/notifications.py b/server/foxbank_server/apis/notifications.py index ab31612..7f39a76 100644 --- a/server/foxbank_server/apis/notifications.py +++ b/server/foxbank_server/apis/notifications.py @@ -1,4 +1,4 @@ -from datetime import datetime +from datetime import datetime, timezone from flask.views import MethodView from flask_smorest import Blueprint from marshmallow import Schema, fields @@ -41,7 +41,7 @@ class NotificationsList(MethodView): The usefulness of this endpoint is questionable besides debugging since it's a notification to self """ - now = datetime.now() + now = datetime.now(timezone.utc).astimezone() notification = Notification.new_notification(body, now, read) insert_notification(decorators.user_id, notification) return returns.success(notification=notification) diff --git a/server/foxbank_server/apis/transactions.py b/server/foxbank_server/apis/transactions.py index aac3dcf..fb545f0 100644 --- a/server/foxbank_server/apis/transactions.py +++ b/server/foxbank_server/apis/transactions.py @@ -1,4 +1,4 @@ -from datetime import date, datetime +from datetime import date, datetime, timezone from flask.views import MethodView from flask_smorest import Blueprint from marshmallow import Schema, fields @@ -75,7 +75,7 @@ class TransactionsList(MethodView): if not check_iban(destination_iban): return returns.abort(returns.INVALID_IBAN) - date = datetime.now() + date = datetime.now(timezone.utc).astimezone() # Check if transaction is to another FoxBank account reverse_transaction = None From 9a1e1b4fce3f488128622860f3cba8e8bb60fdc4 Mon Sep 17 00:00:00 2001 From: Dan Cojocaru Date: Mon, 3 Jan 2022 21:42:21 +0200 Subject: [PATCH 2/3] Added basic currency exchange data --- server/foxbank_server/apis/__init__.py | 2 ++ server/foxbank_server/apis/forex.py | 26 +++++++++++++++++++++++++ server/foxbank_server/db_utils.py | 27 ++++++++++++++++++++++++++ server/init.sql | 7 +++++++ 4 files changed, 62 insertions(+) create mode 100644 server/foxbank_server/apis/forex.py diff --git a/server/foxbank_server/apis/__init__.py b/server/foxbank_server/apis/__init__.py index ab0f6f8..cf10cd0 100644 --- a/server/foxbank_server/apis/__init__.py +++ b/server/foxbank_server/apis/__init__.py @@ -5,6 +5,7 @@ from .accounts import bp as acc_bp from .login import bp as login_bp from .transactions import bp as transactions_bp from .notifications import bp as notifications_bp +from .forex import bp as forex_bp class ApiWithErr(Api): def handle_http_exception(self, error): @@ -31,3 +32,4 @@ def init_apis(app: Flask): api.register_blueprint(acc_bp, url_prefix='/accounts') api.register_blueprint(transactions_bp, url_prefix='/transactions') api.register_blueprint(notifications_bp, url_prefix='/notifications') + api.register_blueprint(forex_bp, url_prefix='/forex') diff --git a/server/foxbank_server/apis/forex.py b/server/foxbank_server/apis/forex.py new file mode 100644 index 0000000..9dc6fbc --- /dev/null +++ b/server/foxbank_server/apis/forex.py @@ -0,0 +1,26 @@ +from flask.views import MethodView +from flask_smorest import Blueprint +from marshmallow import Schema, fields + +from ..db_utils import get_forex_rate +from .. import returns + +bp = Blueprint('forex', __name__, description='Foreign Exchange information') + +class GetExchangeResult(returns.SuccessSchema): + rate = fields.Float(optional=True) + +@bp.get('//') +@bp.response(422, returns.ErrorSchema, description='Invalid currency') +@bp.response(200, GetExchangeResult) +def get_exchange(from_currency: str, to_currency: str): + """Get exchange rate between two currencies""" + if from_currency == to_currency: + rate = 1 + else: + rate = get_forex_rate(from_currency, to_currency) + + if rate is None: + return returns.abort(returns.invalid_argument('currency')) + + return returns.success(rate=rate) diff --git a/server/foxbank_server/db_utils.py b/server/foxbank_server/db_utils.py index 7f02a4d..75d1e59 100644 --- a/server/foxbank_server/db_utils.py +++ b/server/foxbank_server/db_utils.py @@ -306,5 +306,32 @@ class Module(ModuleType): ) self.db.commit() + @get_db + def get_forex_rate(self, from_currency: str, to_currency: str) -> float | None: + cur = self.db.cursor() + + if from_currency == 'RON' or to_currency == 'RON': + currency_pairs = [(from_currency, to_currency)] + else: + currency_pairs = [(from_currency, 'RON'), ('RON', to_currency)] + + amount = 1.0 + for currency_pair in currency_pairs: + to_select = 'to_ron' + if currency_pair[0] == 'RON': + to_select = 'from_ron' + cur.execute( + f'select {to_select} from exchange where currency = ?', + (currency_pair[1] if currency_pair[0] == 'RON' else currency_pair[0],), + ) + rate = cur.fetchone() + if rate is None: + amount = None + break + rate = rate[0] + amount *= rate + + return amount + sys.modules[__name__] = Module(__name__) diff --git a/server/init.sql b/server/init.sql index 7643ad3..f2d1aee 100644 --- a/server/init.sql +++ b/server/init.sql @@ -60,6 +60,13 @@ create table users_notifications ( foreign key (notification_id) references notifications (id) ); +create table exchange ( + id integer primary key autoincrement, + currency text not null, + to_ron real not null, + from_ron real not null +); + create view V_account_balance as select accounts_transactions.account_id as "account_id", From c7c8f3c765c44bd47c779c5206bea9fefe17cad0 Mon Sep 17 00:00:00 2001 From: Dan Cojocaru Date: Mon, 3 Jan 2022 21:49:38 +0200 Subject: [PATCH 3/3] Added transfer between different currency accounts --- server/foxbank_server/apis/transactions.py | 9 ++++++--- server/foxbank_server/db_utils.py | 3 +++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/server/foxbank_server/apis/transactions.py b/server/foxbank_server/apis/transactions.py index fb545f0..3ff730f 100644 --- a/server/foxbank_server/apis/transactions.py +++ b/server/foxbank_server/apis/transactions.py @@ -6,7 +6,7 @@ from marshmallow import Schema, fields import re from ..decorators import ensure_logged_in -from ..db_utils import get_transactions, get_account, get_accounts, insert_transaction, whose_account, insert_notification +from ..db_utils import get_transactions, get_account, get_accounts, insert_transaction, whose_account, insert_notification, get_forex_rate from ..models import Account, Notification, Transaction from ..utils.iban import check_iban from .. import decorators, returns @@ -82,15 +82,18 @@ class TransactionsList(MethodView): if destination_iban[4:8] == 'FOXB': for acc in get_accounts(): if destination_iban == acc.iban: + rate = get_forex_rate(account.currency, acc.currency) reverse_transaction = Transaction.new_transaction( date_time=date, transaction_type='receive_transfer', status='processed', other_party={'iban': account.iban,}, extra={ - 'currency': account.currency, - 'amount': -amount, + 'currency': acc.currency, + 'amount': int(-amount * rate), 'description': description, + 'originalAmount': -amount, + 'originalCurrency': account.currency, }, ) insert_transaction(acc.id, reverse_transaction) diff --git a/server/foxbank_server/db_utils.py b/server/foxbank_server/db_utils.py index 75d1e59..33740ce 100644 --- a/server/foxbank_server/db_utils.py +++ b/server/foxbank_server/db_utils.py @@ -308,6 +308,9 @@ class Module(ModuleType): @get_db def get_forex_rate(self, from_currency: str, to_currency: str) -> float | None: + if from_currency == to_currency: + return 1.0 + cur = self.db.cursor() if from_currency == 'RON' or to_currency == 'RON':