add bank statement
parent
6be9c7ac34
commit
2517e385a6
|
@ -7758,3 +7758,11 @@ img[alpha]:not([alt*="#"]) {
|
|||
td[data-time] {
|
||||
white-space: pre;
|
||||
}
|
||||
|
||||
.bg-green {
|
||||
background-color: #0a6936 !important;
|
||||
}
|
||||
|
||||
.bg-red {
|
||||
background-color: #71000b !important;
|
||||
}
|
||||
|
|
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 50 KiB |
Binary file not shown.
Before Width: | Height: | Size: 50 KiB |
|
@ -34,6 +34,7 @@ from .push_subscriptions import *
|
|||
from .group import *
|
||||
from .group_membership import *
|
||||
from .orgy import *
|
||||
from .currency_logs import *
|
||||
|
||||
if FEATURES['IP_LOGGING']:
|
||||
from .ip_logs import *
|
||||
|
|
|
@ -324,7 +324,7 @@ class Comment(Base):
|
|||
@property
|
||||
@lazy
|
||||
def textlink(self):
|
||||
return f"[this comment]({self.shortlink})"
|
||||
return f'<a href="{self.shortlink}">this comment</a>'
|
||||
|
||||
@property
|
||||
@lazy
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
import time
|
||||
from sqlalchemy import Column, ForeignKey
|
||||
from sqlalchemy.sql.sqltypes import *
|
||||
|
||||
from files.classes import Base
|
||||
from files.helpers.lazy import lazy
|
||||
from files.helpers.sorting_and_time import make_age_string
|
||||
|
||||
class CurrencyLog(Base):
|
||||
__tablename__ = "currency_logs"
|
||||
id = Column(Integer, primary_key=True)
|
||||
user_id = Column(Integer, ForeignKey("users.id"))
|
||||
created_utc = Column(Integer)
|
||||
currency = Column(String)
|
||||
amount = Column(Integer)
|
||||
reason = Column(String)
|
||||
balance = Column(Integer)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
if "created_utc" not in kwargs: kwargs["created_utc"] = int(time.time())
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def __repr__(self):
|
||||
return f"<{self.__class__.__name__}(id={self.id})>"
|
||||
|
||||
@property
|
||||
@lazy
|
||||
def age_string(self):
|
||||
return make_age_string(self.created_utc)
|
|
@ -156,7 +156,7 @@ class Post(Base):
|
|||
@property
|
||||
@lazy
|
||||
def textlink(self):
|
||||
return f"[{self.title}]({self.shortlink})"
|
||||
return f'<a href="{self.shortlink}">{self.title}</a>'
|
||||
|
||||
@property
|
||||
@lazy
|
||||
|
|
|
@ -14,6 +14,7 @@ from files.classes import Base
|
|||
from files.classes.casino_game import CasinoGame
|
||||
from files.classes.group import *
|
||||
from files.classes.hole import Hole
|
||||
from files.classes.currency_logs import CurrencyLog
|
||||
from files.helpers.config.const import *
|
||||
from files.helpers.config.modaction_types import *
|
||||
from files.helpers.config.awards import AWARDS_ENABLED, HOUSE_AWARDS
|
||||
|
@ -213,7 +214,7 @@ class User(Base):
|
|||
def __repr__(self):
|
||||
return f"<{self.__class__.__name__}(id={self.id}, username={self.username})>"
|
||||
|
||||
def pay_account(self, currency, amount):
|
||||
def pay_account(self, currency, amount, reason=None):
|
||||
if self.id in {AUTOJANNY_ID, LONGPOSTBOT_ID, ZOZBOT_ID}:
|
||||
return
|
||||
|
||||
|
@ -238,7 +239,20 @@ class User(Base):
|
|||
else:
|
||||
user_query.update({ User.marseybux: User.marseybux + amount })
|
||||
|
||||
def charge_account(self, currency, amount, **kwargs):
|
||||
if reason and amount:
|
||||
currency_log = CurrencyLog(
|
||||
user_id=self.id,
|
||||
currency=currency,
|
||||
amount=amount,
|
||||
reason=reason,
|
||||
)
|
||||
g.db.add(currency_log)
|
||||
if currency == 'coins':
|
||||
currency_log.balance = self.coins
|
||||
else:
|
||||
currency_log.balance = self.marseybux
|
||||
|
||||
def charge_account(self, currency, amount, reason=None, **kwargs):
|
||||
if self.admin_level >= PERMS['INFINITE_CURRENCY']:
|
||||
return (True, amount)
|
||||
|
||||
|
@ -256,12 +270,14 @@ class User(Base):
|
|||
user_query.update({ User.coins: User.coins - amount })
|
||||
succeeded = True
|
||||
charged_coins = amount
|
||||
logs = ('coins', amount)
|
||||
elif currency == 'marseybux':
|
||||
account_balance = self.marseybux
|
||||
|
||||
if not should_check_balance or account_balance >= amount:
|
||||
user_query.update({ User.marseybux: User.marseybux - amount })
|
||||
succeeded = True
|
||||
logs = ('marseybux', amount)
|
||||
elif currency == 'coins/marseybux':
|
||||
if self.marseybux >= amount:
|
||||
subtracted_mbux = amount
|
||||
|
@ -278,9 +294,24 @@ class User(Base):
|
|||
})
|
||||
succeeded = True
|
||||
charged_coins = subtracted_coins
|
||||
logs = (('coins', subtracted_coins), ('marseybux', subtracted_mbux))
|
||||
|
||||
if succeeded:
|
||||
g.db.add(self)
|
||||
if reason:
|
||||
for currency, amount in logs:
|
||||
if not amount: continue
|
||||
currency_log = CurrencyLog(
|
||||
user_id=self.id,
|
||||
currency=currency,
|
||||
amount=-amount,
|
||||
reason=reason,
|
||||
)
|
||||
g.db.add(currency_log)
|
||||
if currency == 'coins':
|
||||
currency_log.balance = self.coins
|
||||
else:
|
||||
currency_log.balance = self.marseybux
|
||||
|
||||
return (succeeded, charged_coins)
|
||||
|
||||
|
|
|
@ -166,7 +166,7 @@ def execute_snappy(post, v):
|
|||
g.db.add(award_object)
|
||||
|
||||
awarded_coins = int(AWARDS["glowie"]['price'] * COSMETIC_AWARD_COIN_AWARD_PCT)
|
||||
post.author.pay_account('coins', awarded_coins)
|
||||
post.author.pay_account('coins', awarded_coins, f"glowie award on {post.textlink}")
|
||||
|
||||
msg = f"@Snappy has given {post.textlink} the Glowie Award and you have received {awarded_coins} coins as a result!"
|
||||
send_repeatable_notification(post.author.id, msg)
|
||||
|
@ -475,6 +475,7 @@ def execute_antispam_post_check(title, v, url):
|
|||
return True
|
||||
|
||||
def execute_antispam_duplicate_comment_check(v, body_html):
|
||||
return
|
||||
if v.admin_level >= PERMS['BYPASS_ANTISPAM_CHECKS']:
|
||||
return
|
||||
if v.id in ANTISPAM_BYPASS_IDS:
|
||||
|
@ -496,6 +497,7 @@ def execute_antispam_duplicate_comment_check(v, body_html):
|
|||
abort(403, "Too much spam!")
|
||||
|
||||
def execute_antispam_comment_check(body, v):
|
||||
return
|
||||
if v.admin_level >= PERMS['BYPASS_ANTISPAM_CHECKS']:
|
||||
return
|
||||
|
||||
|
|
|
@ -177,6 +177,7 @@ def NOTIFY_USERS(text, v, oldtext=None, ghost=False, obj=None, followers_ping=Tr
|
|||
|
||||
if FEATURES['PING_GROUPS']:
|
||||
cost = 0
|
||||
cost_groups = []
|
||||
coin_receivers = set()
|
||||
|
||||
for i in group_mention_regex.finditer(text):
|
||||
|
@ -194,8 +195,11 @@ def NOTIFY_USERS(text, v, oldtext=None, ghost=False, obj=None, followers_ping=Tr
|
|||
cost = g.db.query(User).count() * 5
|
||||
if cost > v.coins + v.marseybux:
|
||||
abort(403, f"You need {cost} currency to mention these ping groups!")
|
||||
|
||||
v.charge_account('coins/marseybux', cost)
|
||||
|
||||
reason = f"group pinging cost (<code>!everyone</code>)"
|
||||
if obj:
|
||||
reason += f" on {obj.textlink}"
|
||||
v.charge_account('coins/marseybux', cost, reason)
|
||||
if obj:
|
||||
obj.ping_cost += cost
|
||||
return 'everyone'
|
||||
|
@ -231,6 +235,7 @@ def NOTIFY_USERS(text, v, oldtext=None, ghost=False, obj=None, followers_ping=Tr
|
|||
if group and group.name == 'verifiedrich':
|
||||
abort(403, f"Only !verifiedrich members can mention it!")
|
||||
cost += len(members) * 5
|
||||
cost_groups.append(group.name)
|
||||
if cost > v.coins + v.marseybux:
|
||||
abort(403, f"You need {cost} currency to mention these ping groups!")
|
||||
|
||||
|
@ -239,7 +244,10 @@ def NOTIFY_USERS(text, v, oldtext=None, ghost=False, obj=None, followers_ping=Tr
|
|||
|
||||
if charge:
|
||||
if cost:
|
||||
v.charge_account('coins/marseybux', cost)
|
||||
reason = f"group pinging cost (<code>!" + "</code>, <code>!".join(cost_groups) + "</code>)"
|
||||
if obj:
|
||||
reason += f" on {obj.textlink}"
|
||||
v.charge_account('coins/marseybux', cost, reason)
|
||||
if obj:
|
||||
obj.ping_cost += cost
|
||||
|
||||
|
|
|
@ -353,7 +353,7 @@ def _unpin_expired():
|
|||
def _give_marseybux_salary():
|
||||
for u in g.db.query(User).filter(User.admin_level > 0).all():
|
||||
marseybux_salary = u.admin_level * 10000
|
||||
u.pay_account('marseybux', marseybux_salary)
|
||||
u.pay_account('marseybux', marseybux_salary, "janny salary")
|
||||
send_repeatable_notification(u.id, f"You have received your monthly janny salary of {marseybux_salary} Marseybux!")
|
||||
|
||||
def _expire_blocks_mutes_exiles():
|
||||
|
|
|
@ -47,7 +47,7 @@ def end_lottery_session():
|
|||
winner = random.choice(raffle)
|
||||
active_lottery.winner_id = winner
|
||||
winning_user = next(filter(lambda x: x.id == winner, participating_users))
|
||||
winning_user.pay_account('coins', active_lottery.prize)
|
||||
winning_user.pay_account('coins', active_lottery.prize, "lottery winnings")
|
||||
winning_user.total_lottery_winnings += active_lottery.prize
|
||||
badge_grant(user=winning_user, badge_id=LOTTERY_WINNER_BADGE_ID)
|
||||
|
||||
|
@ -109,7 +109,7 @@ def purchase_lottery_tickets(v, quantity=1):
|
|||
if (most_recent_lottery is None):
|
||||
return False, "There is no active lottery!"
|
||||
|
||||
if not v.charge_account('coins', LOTTERY_TICKET_COST * quantity)[0]:
|
||||
if not v.charge_account('coins', LOTTERY_TICKET_COST * quantity, f'cost of {quantity} lottery tickets')[0]:
|
||||
return False, "You don't have enough coins"
|
||||
|
||||
v.currently_held_lottery_tickets += quantity
|
||||
|
|
|
@ -85,7 +85,7 @@ def get_active_roulette_games():
|
|||
|
||||
|
||||
def charge_gambler(gambler, amount, currency):
|
||||
charged = gambler.charge_account(currency, amount)[0]
|
||||
charged = gambler.charge_account(currency, amount, "cost of roulette bet")[0]
|
||||
|
||||
if not charged:
|
||||
raise Exception("Gambler cannot afford charge.")
|
||||
|
@ -179,8 +179,8 @@ def spin_roulette_wheel():
|
|||
coin_winnings = gambler_payout['coins']
|
||||
procoin_winnings = gambler_payout['marseybux']
|
||||
|
||||
gambler.pay_account('coins', coin_winnings)
|
||||
gambler.pay_account('marseybux', procoin_winnings)
|
||||
gambler.pay_account('coins', coin_winnings, "roulette winnings")
|
||||
gambler.pay_account('marseybux', procoin_winnings, "roulette winnings")
|
||||
|
||||
# Notify the winners.
|
||||
notification_text = f"Winning number: {number}\n\nCongratulations! One or more of your roulette bets paid off!\n\n"
|
||||
|
|
|
@ -51,6 +51,19 @@ def casino_slot_pull(gambler, wager_value, currency):
|
|||
g.db.add(casino_game)
|
||||
g.db.flush()
|
||||
|
||||
if casino_game.winnings:
|
||||
currency_log = CurrencyLog(
|
||||
user_id=gambler.id,
|
||||
currency=currency,
|
||||
amount=-casino_game.winnings,
|
||||
reason="slots bet",
|
||||
)
|
||||
g.db.add(currency_log)
|
||||
if currency == 'coins':
|
||||
currency_log.balance = gambler.coins
|
||||
else:
|
||||
currency_log.balance = gambler.marseybux
|
||||
|
||||
return casino_game.id, casino_game.game_state
|
||||
else:
|
||||
return None, "{}",
|
||||
|
|
|
@ -40,5 +40,5 @@ def check_for_treasure(from_comment, in_text):
|
|||
from_comment.treasure_amount = f'l{ticket_count}'
|
||||
return
|
||||
|
||||
user.pay_account('coins', amount)
|
||||
user.pay_account('coins', amount, f"found treasure in {from_comment.textlink}")
|
||||
from_comment.treasure_amount = str(amount)
|
||||
|
|
|
@ -317,9 +317,21 @@ def handle_payout(gambler, state, game):
|
|||
elif split_status == BlackjackStatus.PUSHED:
|
||||
payout += game.wager
|
||||
|
||||
|
||||
gambler.pay_account(game.currency, payout)
|
||||
|
||||
if game.winnings:
|
||||
currency_log = CurrencyLog(
|
||||
user_id=gambler.id,
|
||||
currency=game.currency,
|
||||
amount=-game.winnings,
|
||||
reason="blackjack bet",
|
||||
)
|
||||
g.db.add(currency_log)
|
||||
if currency == 'coins':
|
||||
currency_log.balance = gambler.coins
|
||||
else:
|
||||
currency_log.balance = gambler.marseybux
|
||||
|
||||
if status in {BlackjackStatus.BLACKJACK, BlackjackStatus.WON} or split_status in {BlackjackStatus.WON}:
|
||||
distribute_wager_badges(gambler, game.wager, won=True)
|
||||
elif status == BlackjackStatus.LOST or split_status == BlackjackStatus.LOST:
|
||||
|
|
|
@ -188,7 +188,7 @@ def distribute(v, kind, option_id):
|
|||
cid = notif_comment(text)
|
||||
for vote in votes:
|
||||
u = vote.user
|
||||
u.pay_account('coins', coinsperperson)
|
||||
u.pay_account('coins', coinsperperson, f"bet winnings on {parent.textlink}")
|
||||
add_notif(cid, u.id, text, pushnotif_url=parent.permalink)
|
||||
|
||||
text = f"You lost the {POLL_BET_COINS} coins you bet on {parent.textlink} :marseylaugh:"
|
||||
|
@ -2163,7 +2163,7 @@ def mark_effortpost(pid, v):
|
|||
|
||||
coins = (p.upvotes + p.downvotes) * mul
|
||||
|
||||
p.author.pay_account('coins', coins)
|
||||
p.author.pay_account('coins', coins, f"retroactive efortpost gains of {post.textlink}")
|
||||
|
||||
if v.id != p.author_id:
|
||||
send_repeatable_notification(p.author_id, f":marseyclapping: @{v.username} (a site admin) has marked {p.textlink} as an effortpost, it now gets x{mul} coins from votes. You have received {coins} coins retroactively, thanks! :!marseyclapping:")
|
||||
|
@ -2200,7 +2200,7 @@ def unmark_effortpost(pid, v):
|
|||
|
||||
coins = (p.upvotes + p.downvotes) * mul
|
||||
|
||||
p.author.charge_account('coins', coins)
|
||||
p.author.charge_account('coins', coins, f"revocation of efortpost gains of {post.textlink}")
|
||||
|
||||
if v.id != p.author_id:
|
||||
send_repeatable_notification(p.author_id, f":marseyitsover: @{v.username} (a site admin) has unmarked {p.textlink} as an effortpost. {coins} coins have been deducted from you. :!marseyitsover:")
|
||||
|
|
|
@ -225,7 +225,7 @@ def approve_emoji(v, name):
|
|||
if 'pkmn' in emoji.tags: amount = 500
|
||||
else: amount = 250
|
||||
|
||||
author.pay_account('coins', amount)
|
||||
author.pay_account('coins', amount, f"reward for making :{emoji.name}:")
|
||||
g.db.add(author)
|
||||
|
||||
if v.id != author.id:
|
||||
|
|
|
@ -64,7 +64,7 @@ def buy_award(v, kind, AWARDS):
|
|||
else:
|
||||
currency = 'coins/marseybux'
|
||||
|
||||
charged = v.charge_account(currency, price)
|
||||
charged = v.charge_account(currency, price, f"{kind} award cost")
|
||||
if not charged[0]:
|
||||
abort(400, f"Not enough {currency}!")
|
||||
|
||||
|
@ -238,8 +238,8 @@ def award_thing(v, thing_type, id):
|
|||
|
||||
if kind == 'shit':
|
||||
awarded_coins = int(AWARDS[kind]['price'] * COSMETIC_AWARD_COIN_AWARD_PCT)
|
||||
v.charge_account('coins', awarded_coins, should_check_balance=False)
|
||||
obj.author.pay_account('coins', awarded_coins)
|
||||
v.charge_account('coins', awarded_coins, f"shit award deflected theft on {obj.textlink}", should_check_balance=False)
|
||||
obj.author.pay_account('coins', awarded_coins, f"shit award deflected theft on {obj.textlink}")
|
||||
elif kind != 'spider':
|
||||
if AWARDS[kind]['cosmetic'] and not AWARDS[kind]['included_in_lootbox']:
|
||||
awarded_coins = int(AWARDS[kind]['price'] * COSMETIC_AWARD_COIN_AWARD_PCT)
|
||||
|
@ -248,8 +248,8 @@ def award_thing(v, thing_type, id):
|
|||
|
||||
if awarded_coins:
|
||||
if kind == 'shit':
|
||||
author.charge_account('coins', awarded_coins, should_check_balance=False)
|
||||
v.pay_account('coins', awarded_coins)
|
||||
author.charge_account('coins', awarded_coins, f"shit award theft on {obj.textlink}", should_check_balance=False)
|
||||
v.pay_account('coins', awarded_coins, f"shit award theft on {obj.textlink}")
|
||||
else:
|
||||
author.pay_account('coins', awarded_coins)
|
||||
|
||||
|
@ -479,7 +479,7 @@ def award_thing(v, thing_type, id):
|
|||
author.patron = 1
|
||||
if author.patron_utc: author.patron_utc += 2629746
|
||||
else: author.patron_utc = int(time.time()) + 2629746
|
||||
author.pay_account('marseybux', 1250)
|
||||
author.pay_account('marseybux', 1250, f"benefactor award on {obj.textlink}")
|
||||
badge_grant(user=v, badge_id=103)
|
||||
elif kind == "rehab":
|
||||
if author.rehab: author.rehab += 86400
|
||||
|
|
|
@ -35,7 +35,7 @@ def create_group(v):
|
|||
if name in {'everyone', 'jannies', 'holejannies', 'followers', 'commenters'} or g.db.get(Group, name):
|
||||
abort(400, "This group already exists!")
|
||||
|
||||
if not v.charge_account('coins/marseybux', GROUP_COST)[0]:
|
||||
if not v.charge_account('coins/marseybux', GROUP_COST, f"cost of creating !{name}")[0]:
|
||||
abort(403, "You don't have enough coins or marseybux!")
|
||||
|
||||
g.db.add(v)
|
||||
|
|
|
@ -44,12 +44,12 @@ def buy_hat(v, hat_id):
|
|||
if not hat.is_purchasable:
|
||||
abort(403, "This hat is not for sale!")
|
||||
|
||||
charged = v.charge_account('coins/marseybux', hat.price)
|
||||
charged = v.charge_account('coins/marseybux', hat.price, f"{hat.name} hat cost")
|
||||
if not charged[0]:
|
||||
abort(400, "Not enough coins/marseybux!")
|
||||
|
||||
v.coins_spent_on_hats += charged[1]
|
||||
hat.author.pay_account('coins', hat.price * 0.1)
|
||||
hat.author.pay_account('coins', hat.price * 0.1, f"royalties for `{hat.name}`")
|
||||
|
||||
new_hat = Hat(user_id=v.id, hat_id=hat.id)
|
||||
g.db.add(new_hat)
|
||||
|
|
|
@ -393,7 +393,7 @@ def create_sub2(v):
|
|||
if not hole_group_name_regex.fullmatch(name):
|
||||
abort(400, "Name does not match the required format!")
|
||||
|
||||
if not v.charge_account('coins/marseybux', HOLE_COST)[0]:
|
||||
if not v.charge_account('coins/marseybux', HOLE_COST, f"cost of creating /h/{name}")[0]:
|
||||
abort(400, "You don't have enough coins or marseybux!")
|
||||
|
||||
hole = get_hole(name, graceful=True)
|
||||
|
|
|
@ -26,7 +26,7 @@ def vote_option(option_id, v):
|
|||
if option.exclusive == 2:
|
||||
if option.parent.total_bet_voted(v):
|
||||
abort(403, "You can't participate in a closed bet!")
|
||||
if not v.charge_account('coins/marseybux', POLL_BET_COINS)[0]:
|
||||
if not v.charge_account('coins/marseybux', POLL_BET_COINS, f"cost of bet on {option.parent.textlink}")[0]:
|
||||
abort(400, f"You don't have {POLL_BET_COINS} coins or marseybux!")
|
||||
g.db.add(v)
|
||||
|
||||
|
@ -79,7 +79,7 @@ def vote_option_comment(option_id, v):
|
|||
if option.exclusive == 2:
|
||||
if option.parent.total_bet_voted(v):
|
||||
abort(403, "You can't participate in a closed bet!")
|
||||
if not v.charge_account('coins/marseybux', POLL_BET_COINS)[0]:
|
||||
if not v.charge_account('coins/marseybux', POLL_BET_COINS, f"cost of bet on {option.parent.textlink}")[0]:
|
||||
abort(400, f"You don't have {POLL_BET_COINS} coins or marseybux!")
|
||||
g.db.add(v)
|
||||
|
||||
|
|
|
@ -381,7 +381,7 @@ def settings_personal_post(v):
|
|||
else:
|
||||
cost = HOUSE_JOIN_COST
|
||||
|
||||
success = v.charge_account('coins/marseybux', cost)[0]
|
||||
success = v.charge_account('coins/marseybux', cost, "cost of changing houses")[0]
|
||||
if not success: abort(403)
|
||||
|
||||
if house == "None":
|
||||
|
|
|
@ -78,7 +78,7 @@ def claim_rewards_all_users():
|
|||
marseybux = int(marseybux)
|
||||
text = f"You have received {marseybux} Marseybux! You can use them to buy awards or hats in the [shop](/shop/awards) or gamble them in the [casino](/casino)."
|
||||
|
||||
user.pay_account('marseybux', marseybux)
|
||||
user.pay_account('marseybux', marseybux, f"{patron.lower()} reward")
|
||||
|
||||
send_repeatable_notification(user.id, text)
|
||||
g.db.add(user)
|
||||
|
@ -147,13 +147,13 @@ def transfer_currency(v, username, currency_name, apply_tax):
|
|||
notif_text += f"\n\n> {reason}"
|
||||
log_message += f"\n\n> {reason}"
|
||||
|
||||
if not v.charge_account(currency_name, amount)[0]:
|
||||
if not v.charge_account(currency_name, amount, f"gift to @{username}")[0]:
|
||||
abort(400, f"You don't have enough {currency_name}")
|
||||
|
||||
if currency_name == 'marseybux':
|
||||
receiver.pay_account('marseybux', amount - tax)
|
||||
receiver.pay_account('marseybux', amount - tax, f"gift from @{v.username}")
|
||||
elif currency_name == 'coins':
|
||||
receiver.pay_account('coins', amount - tax)
|
||||
receiver.pay_account('coins', amount - tax, f"gift from @{v.username}")
|
||||
else:
|
||||
raise ValueError(f"Invalid currency '{currency_name}' got when transferring {amount} from {v.id} to {receiver.id}")
|
||||
|
||||
|
@ -1549,3 +1549,18 @@ def usersong(username):
|
|||
@auth_required
|
||||
def user_effortposts(v, username):
|
||||
return redirect(f'/search/posts?q=author:{username}+effortpost:true')
|
||||
|
||||
@app.get("/bank_statement")
|
||||
@limiter.limit(DEFAULT_RATELIMIT, deduct_when=lambda response: response.status_code < 400)
|
||||
@limiter.limit(DEFAULT_RATELIMIT, deduct_when=lambda response: response.status_code < 400, key_func=get_ID)
|
||||
@auth_required
|
||||
def currency_log(v):
|
||||
page = get_page()
|
||||
|
||||
logs = g.db.query(CurrencyLog).filter_by(user_id=v.id)
|
||||
|
||||
total = logs.count()
|
||||
|
||||
logs = logs.order_by(CurrencyLog.created_utc.desc()).offset(PAGE_SIZE * (page - 1)).limit(PAGE_SIZE).all()
|
||||
|
||||
return render_template("bank_statement.html", v=v, logs=logs, page=page, total=total)
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
{% extends "default.html" %}
|
||||
{% block pagetitle %}Bank Statement{% endblock %}
|
||||
{% block content %}
|
||||
<div class="row justify-content-around">
|
||||
<div class="col h-100">
|
||||
<div class="justify-content-between">
|
||||
<h2 class="font-weight-bolder text-center pt-2 pb-3">Bank Statement</h2>
|
||||
</div>
|
||||
<h5 class="font-weight-bolder text-right pb-0 mr-4">Balance</h2>
|
||||
|
||||
<div class="rounded border mx-auto">
|
||||
{% for log in logs %}
|
||||
<div class="modlog-action {% if log.amount > 0 %}bg-green{% else %}bg-red{% endif %} ">
|
||||
<div class="d-flex flex-grow-1 align-items-center">
|
||||
<div class="d-flex align-items-center justify-content-center mr-3 rounded-lg flex-shrink-0" style="width: 32px;height: 32px">
|
||||
<img width="50" alt="{{log.currency}}" class="pl-2" data-bs-toggle="tooltip" data-bs-placement="bottom" src="{{SITE_FULL_IMAGES}}/i/{{log.currency}}.webp?x=7" title="{{log.currency.title()}}">
|
||||
</div>
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="text-muted pl-3">
|
||||
<div>
|
||||
<span><b>{{log.amount}} {{log.currency}}</b> {{log.reason | safe}}</span>
|
||||
</div>
|
||||
<div class="text-gray-500">
|
||||
<span class="log--item-age" data-bs-toggle="tooltip" data-bs-placement="bottom" data-nonce="{{g.nonce}}" data-onmouseover="timestamp(this, '{{log.created_utc}}')">{{log.age_string}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span class="ml-auto">{{log.balance}} {{log.currency}}</span>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="p-3">There's nothing here right now.</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% include "pagination.html" %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
|
@ -337,7 +337,7 @@
|
|||
</div>
|
||||
<div class="text-left pl-2" id="header--username--outerdiv">
|
||||
<div style="color: #{{v.name_color}}" class="text-small font-weight-bold"><span id="header--username" {% if v.patron %}class="patron" style="background-color:#{{v.name_color}}"{% endif %} {% if v.pride_username(v) %}pride_username{% endif %}>{{v.username}}</span></div>
|
||||
<div class="header--currency"><img loading="lazy" alt="coins" class="mr-1 ml-1" data-bs-toggle="tooltip" data-bs-placement="bottom" src="{{'coins.webp' | asset_siteimg}}" title="Coins"><span id="user-coins-amount">{{v.coins}}</span>{% if not FEATURES['MARSEYBUX'] %} Coin{{macros.plural(v.coins)}}{% endif %}</div>
|
||||
<div class="header--currency"><img loading="lazy" alt="coins" class="mr-1 ml-1" data-bs-toggle="tooltip" data-bs-placement="bottom" src="{{SITE_FULL_IMAGES}}/i/coins.webp?x=7" title="Coins"><span id="user-coins-amount">{{v.coins}}</span>{% if not FEATURES['MARSEYBUX'] %} Coin{{macros.plural(v.coins)}}{% endif %}</div>
|
||||
{% if FEATURES['MARSEYBUX'] %}
|
||||
<div class="header--currency"><img loading="lazy" alt="marseybux" class="mr-1 ml-1" data-bs-toggle="tooltip" data-bs-placement="bottom" src="{{SITE_FULL_IMAGES}}/i/marseybux.webp?x=7" title="Marseybux"><span id="user-bux-amount">{{v.marseybux}}</span></div>
|
||||
{% endif %}
|
||||
|
@ -415,7 +415,7 @@
|
|||
{% if v %}
|
||||
<li class="nav-item">
|
||||
<div class="header--currency ml-2 pl-1 my-2">
|
||||
<img loading="lazy" alt="coins" data-bs-toggle="tooltip" data-bs-placement="bottom" src="{{'coins.webp' | asset_siteimg}}" title="Coins">
|
||||
<img loading="lazy" alt="coins" data-bs-toggle="tooltip" data-bs-placement="bottom" src="{{SITE_FULL_IMAGES}}/i/coins.webp?x=7" title="Coins">
|
||||
<span id="user-coins-amount-mobile">{{v.coins}}</span>
|
||||
</div>
|
||||
{% if FEATURES['MARSEYBUX'] %}
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
data-bs-toggle="tooltip"
|
||||
data-bs-placement="bottom"
|
||||
height="13"
|
||||
src="{{'coins.webp' | asset_siteimg}}"
|
||||
src="{{SITE_FULL_IMAGES}}/i/coins.webp?x=7"
|
||||
title="Coins"
|
||||
style="display: none; position: relative; top: -2px"
|
||||
>
|
||||
|
@ -93,7 +93,7 @@
|
|||
data-bs-toggle="tooltip"
|
||||
data-bs-placement="bottom"
|
||||
height="13"
|
||||
src="{{'coins.webp' | asset_siteimg}}"
|
||||
src="{{SITE_FULL_IMAGES}}/i/coins.webp?x=7"
|
||||
title="Coins"
|
||||
>
|
||||
|
||||
|
|
|
@ -106,7 +106,7 @@
|
|||
</div>
|
||||
|
||||
<span id="profile-coins-amount">{{u.coins}}</span>
|
||||
<img loading="lazy" alt="coins" class="ml-1 mb-1 mr-2" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Coins" height="20" src="{{'coins.webp' | asset_siteimg}}">
|
||||
<img loading="lazy" alt="coins" class="ml-1 mb-1 mr-2" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Coins" height="20" src="{{SITE_FULL_IMAGES}}/i/coins.webp?x=7">
|
||||
|
||||
{% if FEATURES['MARSEYBUX'] %}
|
||||
<span id="profile-bux-amount">{{u.marseybux}}</span>
|
||||
|
@ -415,7 +415,7 @@
|
|||
</div>
|
||||
|
||||
<span id="profile-coins-amount-mobile" class="font-weight-bold">{{u.coins}}</span>
|
||||
<img loading="lazy" alt="coins" class="ml-1 mb-1 mr-2" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Coins" height="15" src="{{'coins.webp' | asset_siteimg}}">
|
||||
<img loading="lazy" alt="coins" class="ml-1 mb-1 mr-2" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Coins" height="15" src="{{SITE_FULL_IMAGES}}/i/coins.webp?x=7">
|
||||
|
||||
{% if FEATURES['MARSEYBUX'] %}
|
||||
<span id="profile-bux-amount-mobile" class="font-weight-bold">{{u.marseybux}}</span>
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
create table currency_logs (
|
||||
id integer primary key,
|
||||
user_id integer not null,
|
||||
created_utc integer not null,
|
||||
currency varchar(9) not null,
|
||||
amount integer not null,
|
||||
reason varchar(1000) not null,
|
||||
balance integer not null
|
||||
);
|
||||
|
||||
CREATE SEQUENCE public.currency_logs_id_seq
|
||||
AS integer
|
||||
START WITH 1
|
||||
INCREMENT BY 1
|
||||
NO MINVALUE
|
||||
NO MAXVALUE
|
||||
CACHE 1;
|
||||
|
||||
ALTER SEQUENCE public.currency_logs_id_seq OWNED BY public.currency_logs.id;
|
||||
|
||||
ALTER TABLE ONLY public.currency_logs ALTER COLUMN id SET DEFAULT nextval('public.currency_logs_id_seq'::regclass);
|
||||
|
||||
alter table only currency_logs
|
||||
add constraint currency_logs_user_fkey foreign key (user_id) references public.users(id);
|
||||
|
||||
create index currency_logs_index on currency_logs using btree (user_id);
|
Loading…
Reference in New Issue