def recompute_actual_takes(self, cursor, member=None): """Get the tips and takes for this team and recompute the actual amounts. To avoid deadlocks the given `cursor` should have already acquired an exclusive lock on the `takes` table. """ from liberapay.billing.payday import Payday tips = [ NS(t._asdict()) for t in cursor.all( """ SELECT t.id, t.tipper, t.amount AS full_amount, t.paid_in_advance , ( SELECT basket_sum(w.balance) FROM wallets w WHERE w.owner = t.tipper AND w.is_current AND %(use_mangopay)s ) AS balances , coalesce_currency_amount(( SELECT sum(tr.amount, t.amount::currency) FROM transfers tr WHERE tr.tipper = t.tipper AND tr.team = %(team_id)s AND tr.context = 'take' AND tr.status = 'succeeded' ), t.amount::currency) AS past_transfers_sum FROM current_tips t JOIN participants p ON p.id = t.tipper WHERE t.tippee = %(team_id)s AND t.is_funded AND p.is_suspended IS NOT true """, dict(team_id=self.id, use_mangopay=mangopay.sandbox)) ] takes = [ NS(r._asdict()) for r in (cursor or self.db).all( """ SELECT t.*, p.main_currency, p.accepted_currencies FROM current_takes t JOIN participants p ON p.id = t.member WHERE t.team = %s AND p.is_suspended IS NOT true """, (self.id, )) ] # Recompute the takes transfers, new_leftover = Payday.resolve_takes(tips, takes, self.main_currency) transfers_by_member = group_by(transfers, lambda t: t.member) takes_sum = { k: MoneyBasket(t.amount for t in tr_list) for k, tr_list in transfers_by_member.items() } tippers = { k: set(t.tipper for t in tr_list) for k, tr_list in transfers_by_member.items() } # Update the leftover cursor.run("UPDATE participants SET leftover = %s WHERE id = %s", (new_leftover, self.id)) self.set_attributes(leftover=new_leftover) # Update the cached amounts (actual_amount, taking, and receiving) zero = MoneyBasket() for take in takes: member_id = take.member old_amount = take.actual_amount or zero new_amount = takes_sum.get(take.member, zero) diff = new_amount - old_amount if diff != 0: take.actual_amount = new_amount cursor.run( """ UPDATE takes SET actual_amount = %(actual_amount)s WHERE id = %(id)s """, take.__dict__) ntippers = len(tippers.get(member_id, ())) member_currency, old_taking = cursor.one( "SELECT main_currency, taking FROM participants WHERE id = %s", (member_id, )) diff = diff.fuzzy_sum(member_currency) if old_taking + diff < 0: # Make sure currency fluctuation doesn't result in a negative number diff = -old_taking cursor.run( """ UPDATE participants SET taking = (taking + %(diff)s) , receiving = (receiving + %(diff)s) , nteampatrons = ( CASE WHEN (receiving + %(diff)s) = 0 THEN 0 WHEN nteampatrons < %(ntippers)s THEN %(ntippers)s ELSE nteampatrons END ) WHERE id=%(member_id)s """, dict(member_id=member_id, diff=diff, ntippers=ntippers)) if member and member.id == member_id: r = cursor.one( "SELECT taking, receiving FROM participants WHERE id = %s", (member_id, )) member.set_attributes(**r._asdict()) return takes
def recompute_actual_takes(self, cursor, member=None): """Get the tips and takes for this team and recompute the actual amounts. To avoid deadlocks the given `cursor` should have already acquired an exclusive lock on the `takes` table. """ from liberapay.billing.payday import Payday tips = [NS(t._asdict()) for t in cursor.all(""" SELECT t.id, t.tipper, t.amount AS full_amount, t.paid_in_advance , ( SELECT basket_sum(w.balance) FROM wallets w WHERE w.owner = t.tipper AND w.is_current AND %(use_mangopay)s ) AS balances , coalesce_currency_amount(( SELECT sum(tr.amount, t.amount::currency) FROM transfers tr WHERE tr.tipper = t.tipper AND tr.team = %(team_id)s AND tr.context = 'take' AND tr.status = 'succeeded' ), t.amount::currency) AS past_transfers_sum FROM current_tips t JOIN participants p ON p.id = t.tipper WHERE t.tippee = %(team_id)s AND t.is_funded AND p.is_suspended IS NOT true """, dict(team_id=self.id, use_mangopay=mangopay.sandbox))] takes = [NS(r._asdict()) for r in (cursor or self.db).all(""" SELECT t.*, p.main_currency, p.accepted_currencies FROM current_takes t JOIN participants p ON p.id = t.member WHERE t.team = %s AND p.is_suspended IS NOT true """, (self.id,))] # Recompute the takes transfers, new_leftover = Payday.resolve_takes(tips, takes, self.main_currency) transfers_by_member = group_by(transfers, lambda t: t.member) takes_sum = {k: MoneyBasket(t.amount for t in tr_list) for k, tr_list in transfers_by_member.items()} tippers = {k: set(t.tipper for t in tr_list) for k, tr_list in transfers_by_member.items()} # Update the leftover cursor.run("UPDATE participants SET leftover = %s WHERE id = %s", (new_leftover, self.id)) self.set_attributes(leftover=new_leftover) # Update the cached amounts (actual_amount, taking, and receiving) zero = MoneyBasket() for take in takes: member_id = take.member old_amount = take.actual_amount or zero new_amount = takes_sum.get(take.member, zero) diff = new_amount - old_amount if diff != 0: take.actual_amount = new_amount cursor.run(""" UPDATE takes SET actual_amount = %(actual_amount)s WHERE id = %(id)s """, take.__dict__) ntippers = len(tippers.get(member_id, ())) member_currency, old_taking = cursor.one( "SELECT main_currency, taking FROM participants WHERE id = %s", (member_id,) ) diff = diff.fuzzy_sum(member_currency) if old_taking + diff < 0: # Make sure currency fluctuation doesn't result in a negative number diff = -old_taking cursor.run(""" UPDATE participants SET taking = (taking + %(diff)s) , receiving = (receiving + %(diff)s) , nteampatrons = ( CASE WHEN (receiving + %(diff)s) = 0 THEN 0 WHEN nteampatrons < %(ntippers)s THEN %(ntippers)s ELSE nteampatrons END ) WHERE id=%(member_id)s """, dict(member_id=member_id, diff=diff, ntippers=ntippers)) if member and member.id == member_id: r = cursor.one( "SELECT taking, receiving FROM participants WHERE id = %s", (member_id,) ) member.set_attributes(**r._asdict()) return takes
def recompute_actual_takes(self, cursor, member=None): """Get the tips and takes for this team and recompute the actual amounts. To avoid deadlocks the given `cursor` should have already acquired an exclusive lock on the `takes` table. """ from liberapay.billing.payday import Payday tips = [ NS(t._asdict()) for t in cursor.all( """ SELECT t.id, t.tipper, t.amount AS full_amount , coalesce_currency_amount(( SELECT sum(tr.amount, t.amount::currency) FROM transfers tr WHERE tr.tipper = t.tipper AND tr.team = %(team_id)s AND tr.context = 'take' AND tr.status = 'succeeded' ), t.amount::currency) AS past_transfers_sum FROM current_tips t JOIN participants p ON p.id = t.tipper WHERE t.tippee = %(team_id)s AND t.is_funded AND p.is_suspended IS NOT true """, dict(team_id=self.id)) ] takes = [ NS(r._asdict()) for r in (cursor or self.db).all( """ SELECT t.* FROM current_takes t JOIN participants p ON p.id = t.member WHERE t.team = %s AND p.is_suspended IS NOT true AND p.mangopay_user_id IS NOT NULL """, (self.id, )) ] # Recompute the takes takes_sum = {} tippers = {} transfers, new_leftover = Payday.resolve_takes(tips, takes, self.main_currency) for t in transfers: if t.member in takes_sum: takes_sum[t.member] += t.amount else: takes_sum[t.member] = t.amount if t.member in tippers: tippers[t.member].add(t.tipper) else: tippers[t.member] = set((t.tipper, )) # Update the leftover cursor.run("UPDATE participants SET leftover = %s WHERE id = %s", (new_leftover, self.id)) self.set_attributes(leftover=new_leftover) # Update the cached amounts (actual_amount, taking, and receiving) zero = ZERO[self.main_currency] for take in takes: member_id = take.member old_amount = take.actual_amount or zero new_amount = takes_sum.get(take.member, zero) diff = new_amount - old_amount if diff != 0: take.actual_amount = new_amount cursor.run( """ UPDATE takes SET actual_amount = %(actual_amount)s WHERE id = %(id)s """, take.__dict__) ntippers = len(tippers.get(member_id, ())) member_currency = cursor.one( "SELECT main_currency FROM participants WHERE id = %s", (member_id, )) diff = diff.convert(member_currency) cursor.run( """ UPDATE participants SET taking = (taking + %(diff)s) , receiving = (receiving + %(diff)s) , nteampatrons = ( CASE WHEN (receiving + %(diff)s) = 0 THEN 0 WHEN nteampatrons < %(ntippers)s THEN %(ntippers)s ELSE nteampatrons END ) WHERE id=%(member_id)s """, dict(member_id=member_id, diff=diff, ntippers=ntippers)) if member and member.id == member_id: r = cursor.one( "SELECT taking, receiving FROM participants WHERE id = %s", (member_id, )) member.set_attributes(**r._asdict()) return takes
def recompute_actual_takes(self, cursor, member=None): """Get the tips and takes for this team and recompute the actual amounts. To avoid deadlocks the given `cursor` should have already acquired an exclusive lock on the `takes` table. """ from liberapay.billing.payday import Payday tips = [NS(t._asdict()) for t in cursor.all(""" SELECT t.id, t.tipper, t.amount AS full_amount , coalesce_currency_amount(( SELECT sum(tr.amount, t.amount::currency) FROM transfers tr WHERE tr.tipper = t.tipper AND tr.team = %(team_id)s AND tr.context = 'take' AND tr.status = 'succeeded' ), t.amount::currency) AS past_transfers_sum FROM current_tips t JOIN participants p ON p.id = t.tipper WHERE t.tippee = %(team_id)s AND t.is_funded AND p.is_suspended IS NOT true """, dict(team_id=self.id))] takes = [NS(r._asdict()) for r in (cursor or self.db).all(""" SELECT t.* FROM current_takes t JOIN participants p ON p.id = t.member WHERE t.team = %s AND p.is_suspended IS NOT true AND p.mangopay_user_id IS NOT NULL """, (self.id,))] # Recompute the takes takes_sum = {} tippers = {} transfers, new_leftover = Payday.resolve_takes(tips, takes, self.main_currency) for t in transfers: if t.member in takes_sum: takes_sum[t.member] += t.amount else: takes_sum[t.member] = t.amount if t.member in tippers: tippers[t.member].add(t.tipper) else: tippers[t.member] = set((t.tipper,)) # Update the leftover cursor.run("UPDATE participants SET leftover = %s WHERE id = %s", (new_leftover, self.id)) self.set_attributes(leftover=new_leftover) # Update the cached amounts (actual_amount, taking, and receiving) zero = ZERO[self.main_currency] for take in takes: member_id = take.member old_amount = take.actual_amount or zero new_amount = takes_sum.get(take.member, zero) diff = new_amount - old_amount if diff != 0: take.actual_amount = new_amount cursor.run(""" UPDATE takes SET actual_amount = %(actual_amount)s WHERE id = %(id)s """, take.__dict__) ntippers = len(tippers.get(member_id, ())) member_currency, old_taking = cursor.one( "SELECT main_currency, taking FROM participants WHERE id = %s", (member_id,) ) diff = diff.convert(member_currency) if old_taking + diff < 0: # Make sure currency fluctuation doesn't result in a negative number diff = -old_taking cursor.run(""" UPDATE participants SET taking = (taking + %(diff)s) , receiving = (receiving + %(diff)s) , nteampatrons = ( CASE WHEN (receiving + %(diff)s) = 0 THEN 0 WHEN nteampatrons < %(ntippers)s THEN %(ntippers)s ELSE nteampatrons END ) WHERE id=%(member_id)s """, dict(member_id=member_id, diff=diff, ntippers=ntippers)) if member and member.id == member_id: r = cursor.one( "SELECT taking, receiving FROM participants WHERE id = %s", (member_id,) ) member.set_attributes(**r._asdict()) return takes