feature/pricelist add server pagination for balance #17
|
@ -118,8 +118,12 @@ def modify_user(user, password, new_password=None):
|
||||||
messageController.send_message(messageController.Message(user, text, subject))
|
messageController.send_message(messageController.Message(user, text, subject))
|
||||||
|
|
||||||
|
|
||||||
def get_users():
|
def get_users(userids=None):
|
||||||
return User.query.all()
|
query = User.query
|
||||||
|
if userids:
|
||||||
|
query.filter(User.userid in userids)
|
||||||
|
query = query.order_by(User.lastname.asc(), User.firstname.asc())
|
||||||
|
return query.all()
|
||||||
|
|
||||||
|
|
||||||
def get_user_by_role(role: Role):
|
def get_user_by_role(role: Role):
|
||||||
|
@ -175,8 +179,8 @@ def register(data):
|
||||||
allowed_keys = User().serialize().keys()
|
allowed_keys = User().serialize().keys()
|
||||||
values = {key: value for key, value in data.items() if key in allowed_keys}
|
values = {key: value for key, value in data.items() if key in allowed_keys}
|
||||||
roles = values.pop("roles", [])
|
roles = values.pop("roles", [])
|
||||||
if "birthday" in values:
|
if "birthday" in data:
|
||||||
values["birthday"] = from_iso_format(values["birthday"]).date()
|
values["birthday"] = from_iso_format(data["birthday"]).date()
|
||||||
user = User(**values)
|
user = User(**values)
|
||||||
set_roles(user, roles)
|
set_roles(user, roles)
|
||||||
|
|
||||||
|
@ -195,6 +199,8 @@ def register(data):
|
||||||
)
|
)
|
||||||
messageController.send_message(messageController.Message(user, text, subject))
|
messageController.send_message(messageController.Message(user, text, subject))
|
||||||
|
|
||||||
|
find_user(user.userid)
|
||||||
|
|
||||||
return user
|
return user
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -104,6 +104,11 @@ class User(db.Model, ModelSerializeMixin):
|
||||||
def has_permission(self, permission):
|
def has_permission(self, permission):
|
||||||
return permission in self.get_permissions()
|
return permission in self.get_permissions()
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return (
|
||||||
|
f"User({self.userid}, {self.firstname}, {self.lastname}, {self.mail}, {self.display_name}, {self.birthday})"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class _UserAttribute(db.Model, ModelSerializeMixin):
|
class _UserAttribute(db.Model, ModelSerializeMixin):
|
||||||
__tablename__ = "user_attribute"
|
__tablename__ = "user_attribute"
|
||||||
|
|
|
@ -3,12 +3,13 @@
|
||||||
# English: Debit -> from account
|
# English: Debit -> from account
|
||||||
# Credit -> to account
|
# Credit -> to account
|
||||||
|
|
||||||
from sqlalchemy import func
|
from sqlalchemy import func, case, and_
|
||||||
|
from sqlalchemy.ext.hybrid import hybrid_property
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from werkzeug.exceptions import BadRequest, NotFound, Conflict
|
from werkzeug.exceptions import BadRequest, NotFound, Conflict
|
||||||
|
|
||||||
from flaschengeist.database import db
|
from flaschengeist.database import db
|
||||||
from flaschengeist.models.user import User
|
from flaschengeist.models.user import User, _UserAttribute
|
||||||
|
|
||||||
from .models import Transaction
|
from .models import Transaction
|
||||||
from . import permissions, BalancePlugin
|
from . import permissions, BalancePlugin
|
||||||
|
@ -38,27 +39,114 @@ def get_balance(user, start: datetime = None, end: datetime = None):
|
||||||
return credit, debit, credit - debit
|
return credit, debit, credit - debit
|
||||||
|
|
||||||
|
|
||||||
def get_balances(start: datetime = None, end: datetime = None):
|
def get_balances(start: datetime = None, end: datetime = None, limit=None, offset=None, descending=None, sortBy=None):
|
||||||
debit = db.session.query(Transaction.sender_id, func.sum(Transaction.amount)).filter(Transaction.sender_ != None)
|
class _User(User):
|
||||||
credit = db.session.query(Transaction.receiver_id, func.sum(Transaction.amount)).filter(
|
_debit = db.relationship(Transaction, back_populates="sender_", foreign_keys=[Transaction._sender_id])
|
||||||
Transaction.receiver_ != None
|
_credit = db.relationship(Transaction, back_populates="receiver_", foreign_keys=[Transaction._receiver_id])
|
||||||
)
|
|
||||||
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()
|
@hybrid_property
|
||||||
credit = credit.group_by(Transaction._receiver_id).all()
|
def debit(self):
|
||||||
|
return sum([cred.amount for cred in self._debit])
|
||||||
|
|
||||||
|
@debit.expression
|
||||||
|
def debit(cls):
|
||||||
|
a = (
|
||||||
|
db.select(func.sum(Transaction.amount))
|
||||||
|
.where(cls.id_ == Transaction._sender_id, Transaction.amount)
|
||||||
|
.scalar_subquery()
|
||||||
|
)
|
||||||
|
return case([(a, a)], else_=0)
|
||||||
|
|
||||||
|
@hybrid_property
|
||||||
|
def credit(self):
|
||||||
|
return sum([cred.amount for cred in self._credit])
|
||||||
|
|
||||||
|
@credit.expression
|
||||||
|
def credit(cls):
|
||||||
|
b = (
|
||||||
|
db.select(func.sum(Transaction.amount))
|
||||||
|
.where(cls.id_ == Transaction._receiver_id, Transaction.amount)
|
||||||
|
.scalar_subquery()
|
||||||
|
)
|
||||||
|
return case([(b, b)], else_=0)
|
||||||
|
|
||||||
|
@hybrid_property
|
||||||
|
def limit(self):
|
||||||
|
return self.get_attribute("balance_limit", None)
|
||||||
|
|
||||||
|
@limit.expression
|
||||||
|
def limit(cls):
|
||||||
|
return (
|
||||||
|
db.select(_UserAttribute.value)
|
||||||
|
.where(and_(cls.id_ == _UserAttribute.user, _UserAttribute.name == "balance_limit"))
|
||||||
|
.scalar_subquery()
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_debit(self, start: datetime = None, end: datetime = None):
|
||||||
|
if start and end:
|
||||||
|
return sum([deb.amount for deb in self._debit if start <= deb.time and deb.time <= end])
|
||||||
|
if start:
|
||||||
|
return sum([deb.amount for deb in self._dedit if start <= deb.time])
|
||||||
|
if end:
|
||||||
|
return sum([deb.amount for deb in self._dedit if deb.time <= end])
|
||||||
|
return self.debit
|
||||||
|
|
||||||
|
def get_credit(self, start: datetime = None, end: datetime = None):
|
||||||
|
if start and end:
|
||||||
|
return sum([cred.amount for cred in self._credit if start <= cred.time and cred.time <= end])
|
||||||
|
if start:
|
||||||
|
return sum([cred.amount for cred in self._credit if start <= cred.time])
|
||||||
|
if end:
|
||||||
|
return sum([cred.amount for cred in self._credit if cred.time <= end])
|
||||||
|
return self.credit
|
||||||
|
|
||||||
|
query = _User.query
|
||||||
|
|
||||||
|
if start:
|
||||||
|
q1 = query.join(_User._credit).filter(start <= Transaction.time)
|
||||||
|
q2 = query.join(_User._debit).filter(start <= Transaction.time)
|
||||||
|
query = q1.union(q2)
|
||||||
|
if end:
|
||||||
|
q1 = query.join(_User._credit).filter(Transaction.time <= end)
|
||||||
|
q2 = query.join(_User._debit).filter(Transaction.time <= end)
|
||||||
|
query = q1.union(q2)
|
||||||
|
|
||||||
|
if sortBy == "balance":
|
||||||
|
if descending:
|
||||||
|
query = query.order_by((_User.credit - _User.debit).desc(), _User.lastname.asc(), _User.firstname.asc())
|
||||||
|
else:
|
||||||
|
query = query.order_by((_User.credit - _User.debit).asc(), _User.lastname.asc(), _User.firstname.asc())
|
||||||
|
elif sortBy == "limit":
|
||||||
|
if descending:
|
||||||
|
query = query.order_by(_User.limit.desc(), User.lastname.asc(), User.firstname.asc())
|
||||||
|
else:
|
||||||
|
query = query.order_by(_User.limit.asc(), User.lastname.asc(), User.firstname.asc())
|
||||||
|
elif sortBy == "firstname":
|
||||||
|
if descending:
|
||||||
|
query = query.order_by(User.firstname.desc(), User.lastname.desc())
|
||||||
|
else:
|
||||||
|
query = query.order_by(User.firstname.asc(), User.lastname.asc())
|
||||||
|
elif sortBy == "lastname":
|
||||||
|
if descending:
|
||||||
|
query = query.order_by(User.lastname.desc(), User.firstname.desc())
|
||||||
|
else:
|
||||||
|
query = query.order_by(User.lastname.asc(), User.firstname.asc())
|
||||||
|
|
||||||
|
count = None
|
||||||
|
if limit:
|
||||||
|
count = query.count()
|
||||||
|
query = query.limit(limit)
|
||||||
|
if offset:
|
||||||
|
query = query.offset(offset)
|
||||||
|
users = query
|
||||||
all = {}
|
all = {}
|
||||||
for uid, cred in credit:
|
|
||||||
all[uid] = [cred, 0]
|
for user in users:
|
||||||
for uid, deb in debit:
|
|
||||||
all.setdefault(uid, [0, 0])
|
all[user.userid] = [user.get_credit(start, end), 0]
|
||||||
all[uid][1] = deb
|
all[user.userid][1] = user.get_debit(start, end)
|
||||||
return all
|
|
||||||
|
return all, count
|
||||||
|
|
||||||
|
|
||||||
def send(sender: User, receiver, amount: float, author: User):
|
def send(sender: User, receiver, amount: float, author: User):
|
||||||
|
|
|
@ -110,8 +110,10 @@ def limits(current_session: Session):
|
||||||
Returns:
|
Returns:
|
||||||
JSON encoded array of userid with limit or HTTP-error
|
JSON encoded array of userid with limit or HTTP-error
|
||||||
"""
|
"""
|
||||||
|
userids = None
|
||||||
users = userController.get_users()
|
if "userids" in request.args:
|
||||||
|
[x for x in request.args.get("userids").split(",") if x]
|
||||||
|
users = userController.get_users(userids=userids)
|
||||||
if request.method == "GET":
|
if request.method == "GET":
|
||||||
return jsonify([{"userid": user.userid, "limit": user.get_attribute("balance_limit")} for user in users])
|
return jsonify([{"userid": user.userid, "limit": user.get_attribute("balance_limit")} for user in users])
|
||||||
|
|
||||||
|
@ -311,5 +313,11 @@ def get_balances(current_session: Session):
|
||||||
Returns:
|
Returns:
|
||||||
JSON Array containing credit, debit and userid for each user or HTTP error
|
JSON Array containing credit, debit and userid for each user or HTTP error
|
||||||
"""
|
"""
|
||||||
balances = balance_controller.get_balances()
|
limit = request.args.get("limit", type=int)
|
||||||
return jsonify([{"userid": u, "credit": v[0], "debit": v[1]} for u, v in balances.items()])
|
offset = request.args.get("offset", type=int)
|
||||||
|
descending = request.args.get("descending", False, type=bool)
|
||||||
|
sortBy = request.args.get("sortBy", type=str)
|
||||||
|
balances, count = balance_controller.get_balances(limit=limit, offset=offset, descending=descending, sortBy=sortBy)
|
||||||
|
return jsonify(
|
||||||
|
{"balances": [{"userid": u, "credit": v[0], "debit": v[1]} for u, v in balances.items()], "count": count}
|
||||||
|
)
|
||||||
|
|
|
@ -69,7 +69,10 @@ def list_users(current_session):
|
||||||
JSON encoded array of `flaschengeist.models.user.User` or HTTP error
|
JSON encoded array of `flaschengeist.models.user.User` or HTTP error
|
||||||
"""
|
"""
|
||||||
logger.debug("Retrieve list of all users")
|
logger.debug("Retrieve list of all users")
|
||||||
users = userController.get_users()
|
userids = None
|
||||||
|
if "userids" in request.args:
|
||||||
|
userids = [x for x in request.args.get("userids").split(",") if x]
|
||||||
|
users = userController.get_users(userids=userids)
|
||||||
return jsonify(users)
|
return jsonify(users)
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue