flaschengeist/flaschengeist/plugins/balance/__init__.py

204 lines
6.4 KiB
Python

"""Balance plugin
Extends users plugin with balance functions
"""
from datetime import datetime, timezone
from flask import Blueprint, request
from werkzeug.exceptions import Forbidden, BadRequest
from flaschengeist import logger
from flaschengeist.utils import HTTP
from flaschengeist.models.session import Session
from flaschengeist.utils.datetime import from_iso_format
from flaschengeist.decorator import login_required
from flaschengeist.controller import userController
from flaschengeist.plugins import Plugin, before_update_user
from . import balance_controller, permissions, models
balance_bp = Blueprint("balance", __name__)
class BalancePlugin(Plugin):
models = models
def __init__(self, config):
super().__init__(blueprint=balance_bp, permissions=permissions.permissions)
@before_update_user
def set_default_limit(user):
if "limit" in config:
limit = config["limit"]
logger.debug("Setting default limit of {} to user {}".format(limit, user.userid))
balance_controller.set_limit(user, limit, override=False)
def install(self):
from flaschengeist.database import db
db.create_all()
@balance_bp.route("/users/<userid>/balance/limit", methods=["GET"])
@login_required()
def get_limit(userid, current_session: Session):
"""Get set limit of an user
Route: ``/users/<userid>/balance/limit`` | Method: ``GET``
Args:
userid: Userid identifying the user
current_session: Session sent with Authorization Header
Returns:
JSON object containing the limit (or Null if no limit set) or HTTP error
"""
user = userController.get_user(userid)
if (user != current_session._user and not current_session._user.has_permission(permissions.SET_LIMIT)) or (
user == current_session._user and not user.has_permission(permissions.SHOW)
):
raise Forbidden
return {"limit": balance_controller.get_limit(user)}
@balance_bp.route("/users/<userid>/balance/limit", methods=["PUT"])
@login_required()
def set_limit(userid, current_session: Session):
"""Set the limit of an user
Route: ``/users/<userid>/balance/limit`` | Method: ``PUT``
POST-data: ``{limit: float}``
Args:
userid: Userid identifying the user
current_session: Session sent with Authorization Header
Returns:
HTTP-200 or HTTP error
"""
user = userController.get_user(userid)
if not current_session._user.has_permission(permissions.SET_LIMIT):
raise Forbidden
data = request.get_json()
try:
limit = data["limit"]
except (TypeError, KeyError):
raise BadRequest
balance_controller.set_limit(user, limit)
return HTTP.no_content()
@balance_bp.route("/users/<userid>/balance", methods=["GET"])
@login_required(permission=permissions.SHOW)
def get_balance(userid, current_session: Session):
"""Get balance of user, optionally filtered
Route: ``/users/<userid>/balance`` | Method: ``GET``
GET-parameters: ```{from?: string, to?: string}```
Args:
userid: Userid of user to get balance from
current_session: Session sent with Authorization Header
Returns:
JSON object containing credit, debit and balance or HTTP error
"""
if userid != current_session._user.userid and not current_session._user.has_permission(permissions.SHOW_OTHER):
raise Forbidden
# Might raise NotFound
user = userController.get_user(userid)
start = request.args.get("from")
if start:
start = from_iso_format(start)
else:
start = datetime.fromtimestamp(0, tz=timezone.utc)
end = request.args.get("to")
if end:
end = from_iso_format(end)
else:
end = datetime.now(tz=timezone.utc)
balance = balance_controller.get_balance(user, start, end)
return {"credit": balance[0], "debit": balance[1], "balance": balance[2]}
@balance_bp.route("/users/<userid>/balance", methods=["PUT"])
@login_required()
def change_balance(userid, current_session: Session):
"""Change balance of an user
If ``sender`` is preset in POST-data, the action is handled as a transfer from ``sender`` to user.
Route: ``/users/<userid>/balance`` | Method: ``PUT``
POST-data: ``{amount: float, sender: string}``
Args:
userid: userid identifying user to change balance
current_session: Session sent with Authorization Header
Returns:
JSON encoded transaction (201) or HTTP error
"""
data = request.get_json()
try:
amount = data["amount"]
except (TypeError, KeyError):
raise BadRequest
sender = data.get("sender", None)
user = userController.get_user(userid)
if sender:
sender = userController.get_user(sender)
if sender == user:
raise BadRequest
if (sender == current_session._user and sender.has_permission(permissions.SEND)) or (
sender != current_session._user and current_session._user.has_permission(permissions.SEND_OTHER)
):
return HTTP.created(balance_controller.send(sender, user, amount, current_session._user))
elif (
amount < 0
and (
(user == current_session._user and user.has_permission(permissions.DEBIT_OWN))
or current_session._user.has_permission(permissions.DEBIT)
)
) or (amount > 0 and current_session._user.has_permission(permissions.CREDIT)):
return HTTP.created(balance_controller.change_balance(user, data["amount"], current_session._user))
raise Forbidden
@balance_bp.route("/balance/<int:transaction_id>", methods=["DELETE"])
@login_required()
def reverse_transaction(transaction_id, current_session: Session):
"""Reverse a transaction
Route: ``/balance/<int:transaction_id>`` | Method: ``DELETE``
Args:
transaction_id: Identifier of the transaction
current_session: Session sent with Authorization Header
Returns:
JSON encoded reversal (transaction) (201) or HTTP error
"""
transaction = balance_controller.get_transaction(transaction_id)
if current_session._user.has_permission(permissions.REVERSAL) or (
transaction.sender_ == current_session._user
and (datetime.now(tz=timezone.utc) - transaction.time).total_seconds() < 10
):
reversal = balance_controller.reverse_transaction(transaction, current_session._user)
return HTTP.created(reversal)
raise Forbidden