157 lines
5.1 KiB
Python
157 lines
5.1 KiB
Python
# German: Soll -> Abgang vom Konto
|
|
# Haben -> Zugang aufs Konto
|
|
# English: Debit -> from account
|
|
# Credit -> to account
|
|
|
|
from sqlalchemy import func
|
|
from datetime import datetime
|
|
from werkzeug.exceptions import BadRequest, NotFound, Conflict
|
|
|
|
from flaschengeist.database import db
|
|
from flaschengeist.models.user import User
|
|
|
|
from .models import Transaction
|
|
from . import permissions, BalancePlugin
|
|
|
|
__attribute_limit = "balance_limit"
|
|
|
|
|
|
def set_limit(user: User, limit: float, override=True):
|
|
if override or not user.has_attribute(__attribute_limit):
|
|
user.set_attribute(__attribute_limit, limit)
|
|
db.session.commit()
|
|
|
|
|
|
def get_limit(user: User) -> float:
|
|
return user.get_attribute(__attribute_limit, default=None)
|
|
|
|
|
|
def get_balance(user, start: datetime = None, end: datetime = None):
|
|
query = db.session.query(func.sum(Transaction.amount))
|
|
if start:
|
|
query = query.filter(start <= Transaction.time)
|
|
if end:
|
|
query = query.filter(Transaction.time <= end)
|
|
|
|
credit = query.filter(Transaction.receiver_ == user).scalar() or 0
|
|
debit = query.filter(Transaction.sender_ == user).scalar() or 0
|
|
return credit, debit, credit - debit
|
|
|
|
|
|
def get_balances(start: datetime = None, end: datetime = None):
|
|
debit = db.session.query(Transaction.sender_id, func.sum(Transaction.amount)).filter(Transaction.sender_ != None)
|
|
credit = db.session.query(Transaction.receiver_id, func.sum(Transaction.amount)).filter(
|
|
Transaction.receiver_ != None
|
|
)
|
|
if start:
|
|
debit = debit.filter(start <= Transaction.time)
|
|
credit = credit.filter(start <= Transaction.time)
|
|
if end:
|
|
debit = debit.filter(Transaction.time <= end)
|
|
credit = credit.filter(Transaction.time <= end)
|
|
|
|
debit = debit.group_by(Transaction._sender_id).all()
|
|
credit = credit.group_by(Transaction._receiver_id).all()
|
|
all = {}
|
|
for uid, cred in credit:
|
|
all[uid] = [cred, 0]
|
|
for uid, deb in debit:
|
|
all.setdefault(uid, [0, 0])
|
|
all[uid][1] = deb
|
|
return all
|
|
|
|
|
|
def send(sender: User, receiver, amount: float, author: User):
|
|
"""Send credit from one user to an other
|
|
|
|
Args:
|
|
sender: User who sends the amount
|
|
receiver: User who receives the amount
|
|
amount: Amount to send
|
|
author: User authoring this transaction
|
|
Returns:
|
|
Transaction that was created
|
|
Raises:
|
|
BadRequest if amount <= 0
|
|
"""
|
|
if amount <= 0:
|
|
raise BadRequest
|
|
|
|
if sender and sender.has_attribute(__attribute_limit):
|
|
if (get_balance(sender)[2] - amount) < sender.get_attribute(__attribute_limit) and not author.has_permission(
|
|
permissions.EXCEED_LIMIT
|
|
):
|
|
raise Conflict("Limit exceeded")
|
|
|
|
transaction = Transaction(sender_=sender, receiver_=receiver, amount=amount, author_=author)
|
|
db.session.add(transaction)
|
|
db.session.commit()
|
|
if sender is not None and sender.id_ != author.id_:
|
|
BalancePlugin.plugin.notify(sender, "Neue Transaktion")
|
|
if receiver is not None and receiver.id_ != author.id_:
|
|
BalancePlugin.plugin.notify(receiver, "Neue Transaktion")
|
|
return transaction
|
|
|
|
|
|
def change_balance(user, amount: float, author):
|
|
"""Change balance of user
|
|
|
|
Args:
|
|
user: User to change balance
|
|
amount: Amount to change balance
|
|
author: User authoring this transaction
|
|
"""
|
|
sender = user if amount < 0 else None
|
|
receiver = user if amount > 0 else None
|
|
return send(sender, receiver, abs(amount), author)
|
|
|
|
|
|
def get_transaction(transaction_id) -> Transaction:
|
|
transaction = Transaction.query.get(transaction_id)
|
|
if not transaction:
|
|
raise NotFound
|
|
return transaction
|
|
|
|
|
|
def get_transactions(
|
|
user, start=None, end=None, limit=None, offset=None, show_reversal=False, show_cancelled=True, descending=False
|
|
):
|
|
count = None
|
|
query = Transaction.query.filter((Transaction.sender_ == user) | (Transaction.receiver_ == user))
|
|
if start:
|
|
query = query.filter(start <= Transaction.time)
|
|
if end:
|
|
query = query.filter(Transaction.time <= end)
|
|
# Do not show reversals if disabled or cancelled ones are hidden
|
|
if not show_reversal or not show_cancelled:
|
|
query = query.filter(Transaction.original_ == None)
|
|
if not show_cancelled:
|
|
query = query.filter(Transaction.reversal_id.is_(None))
|
|
if descending:
|
|
query = query.order_by(Transaction.time.desc())
|
|
else:
|
|
query = query.order_by(Transaction.time)
|
|
if limit is not None:
|
|
count = query.count()
|
|
query = query.limit(limit)
|
|
if offset is not None:
|
|
query = query.offset(offset)
|
|
|
|
return query.all(), count
|
|
|
|
|
|
def reverse_transaction(transaction: Transaction, author: User):
|
|
"""Reverse a transaction
|
|
|
|
Args:
|
|
transaction: Transaction to reverse
|
|
author: User that wants the transaction to be reverted
|
|
"""
|
|
if transaction.reversal_:
|
|
raise Conflict
|
|
reversal = send(transaction.receiver_, transaction.sender_, transaction.amount, author)
|
|
reversal.original_ = transaction
|
|
transaction.reversal = reversal
|
|
db.session.commit()
|
|
return reversal
|