Skip to content
This repository has been archived by the owner on Feb 8, 2018. It is now read-only.

rename subscriptions to payment_instructions #3652

Merged
merged 8 commits into from
Jul 30, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions gratipay/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ class NotSane(Exception):
# gratipay.postgres.PostgresManager.


MAX_TIP = Decimal('1000.00')
MIN_TIP = Decimal('0.00')
MAX_TIP = MAX_PAYMENT = Decimal('1000.00')
MIN_TIP = MIN_PAYMENT = Decimal('0.00')

RESTRICTED_IDS = None

Expand Down
17 changes: 9 additions & 8 deletions gratipay/billing/payday.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ class Payday(object):
payin
prepare
create_card_holds
process_subscriptions
process_payment_instructions
transfer_takes
process_draws
settle_card_holds
Expand Down Expand Up @@ -156,7 +156,7 @@ def payin(self):
with self.db.get_cursor() as cursor:
self.prepare(cursor, self.ts_start)
holds = self.create_card_holds(cursor)
self.process_subscriptions(cursor)
self.process_payment_instructions(cursor)
self.transfer_takes(cursor, self.ts_start)
self.process_draws(cursor)
payments = cursor.all("""
Expand Down Expand Up @@ -259,11 +259,12 @@ def f(p):


@staticmethod
def process_subscriptions(cursor):
"""Trigger the process_subscription function for each row in payday_subscriptions.
def process_payment_instructions(cursor):
"""Trigger the process_payment_instructions function for each row in
payday_payment_instructions.
"""
log("Processing subscriptions.")
cursor.run("UPDATE payday_subscriptions SET is_funded=true;")
log("Processing payment instructions.")
cursor.run("UPDATE payday_payment_instructions SET is_funded=true;")


@staticmethod
Expand Down Expand Up @@ -503,9 +504,9 @@ def notify_participants(self):
WITH tippees AS (
SELECT t.slug, amount
FROM ( SELECT DISTINCT ON (team) team, amount
FROM subscriptions
FROM payment_instructions
WHERE mtime < %(ts_start)s
AND subscriber = %(username)s
AND participant = %(username)s
ORDER BY team, mtime DESC
) s
JOIN teams t ON s.team = t.slug
Expand Down
77 changes: 39 additions & 38 deletions gratipay/models/participant.py
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,7 @@ def close(self):
"""Close the participant's account.
"""
with self.db.get_cursor() as cursor:
self.clear_subscriptions(cursor)
self.clear_payment_instructions(cursor)
self.clear_personal_information(cursor)
self.final_check(cursor)
self.update_is_closed(True, cursor)
Expand All @@ -341,22 +341,22 @@ def update_is_closed(self, is_closed, cursor=None):
self.set_attributes(is_closed=is_closed)


def clear_subscriptions(self, cursor):
"""Zero out the participant's subscriptions.
def clear_payment_instructions(self, cursor):
"""Zero out the participant's payment_instructions.
"""
teams = cursor.all("""

SELECT ( SELECT teams.*::teams
FROM teams
WHERE slug=team
) AS team
FROM current_subscriptions
WHERE subscriber = %s
FROM current_payment_instructions
WHERE participant = %s
AND amount > 0

""", (self.username,))
for team in teams:
self.set_subscription_to(team, '0.00', update_self=False, cursor=cursor)
self.set_payment_instruction(team, '0.00', update_self=False, cursor=cursor)


def clear_takes(self, cursor):
Expand Down Expand Up @@ -934,9 +934,9 @@ def update_giving(self, cursor=None):
# Update is_funded on tips
if self.get_credit_card_error() == '':
updated = (cursor or self.db).all("""
UPDATE current_subscriptions
UPDATE current_payment_instructions
SET is_funded = true
WHERE subscriber = %s
WHERE participant = %s
AND is_funded IS NOT true
RETURNING *
""", (self.username,))
Expand All @@ -945,9 +945,9 @@ def update_giving(self, cursor=None):
UPDATE participants p
SET giving = COALESCE((
SELECT sum(amount)
FROM current_subscriptions s
FROM current_payment_instructions s
JOIN teams t ON t.slug=s.team
WHERE subscriber=%(username)s
WHERE participant=%(username)s
AND amount > 0
AND is_funded
AND t.is_approved
Expand Down Expand Up @@ -1001,16 +1001,17 @@ def update_is_free_rider(self, is_free_rider, cursor=None):

# New payday system

def set_subscription_to(self, team, amount, update_self=True, update_team=True, cursor=None):
"""Given a Team or username, and amount as str, returns a dict.
def set_payment_instruction(self, team, amount, update_self=True, update_team=True,
cursor=None):
"""Given a Team or slug, and amount as str, returns a dict.

We INSERT instead of UPDATE, so that we have history to explore. The
COALESCE function returns the first of its arguments that is not NULL.
The effect here is to stamp all tips with the timestamp of the first
tip from this user to that. I believe this is used to determine the
order of payments during payday.
The effect here is to stamp all payment instructions with the timestamp
of the first instruction from this ~user to that Team. I believe this
is used to determine the order of payments during payday.

The dict returned represents the row inserted in the subscriptions
The dict returned represents the row inserted in the payment_instructions
table.

"""
Expand All @@ -1022,41 +1023,41 @@ def set_subscription_to(self, team, amount, update_self=True, update_team=True,
raise NoTeam(slug)

amount = Decimal(amount) # May raise InvalidOperation
if (amount < gratipay.MIN_TIP) or (amount > gratipay.MAX_TIP):
if (amount < gratipay.MIN_PAYMENT) or (amount > gratipay.MAX_PAYMENT):
raise BadAmount

# Insert subscription
NEW_SUBSCRIPTION = """\
# Insert payment instruction
NEW_PAYMENT_INSTRUCTION = """\

INSERT INTO subscriptions
(ctime, subscriber, team, amount)
INSERT INTO payment_instructions
(ctime, participant, team, amount)
VALUES ( COALESCE (( SELECT ctime
FROM subscriptions
WHERE (subscriber=%(subscriber)s AND team=%(team)s)
FROM payment_instructions
WHERE (participant=%(participant)s AND team=%(team)s)
LIMIT 1
), CURRENT_TIMESTAMP)
, %(subscriber)s, %(team)s, %(amount)s
, %(participant)s, %(team)s, %(amount)s
)
RETURNING *

"""
args = dict(subscriber=self.username, team=team.slug, amount=amount)
t = (cursor or self.db).one(NEW_SUBSCRIPTION, args)
args = dict(participant=self.username, team=team.slug, amount=amount)
t = (cursor or self.db).one(NEW_PAYMENT_INSTRUCTION, args)

if update_self:
# Update giving amount of subscriber
# Update giving amount of participant
self.update_giving(cursor)
if update_team:
# Update receiving amount of team
team.update_receiving(cursor)
if team.slug == 'Gratipay':
# Update whether the subscriber is using Gratipay for free
# Update whether the participant is using Gratipay for free
self.update_is_free_rider(None if amount == 0 else False, cursor)

return t._asdict()


def get_subscription_to(self, team):
def get_payment_instruction(self, team):
"""Given a slug, returns a dict.
"""

Expand All @@ -1069,8 +1070,8 @@ def get_subscription_to(self, team):
return self.db.one("""\

SELECT *
FROM subscriptions
WHERE subscriber=%s
FROM payment_instructions
WHERE participant=%s
AND team=%s
ORDER BY mtime DESC
LIMIT 1
Expand Down Expand Up @@ -1218,11 +1219,11 @@ def get_tip_distribution(self):
return tip_amounts, npatrons, contributed


def get_subscriptions_for_profile(self):
def get_giving_for_profile(self):
"""Return a list and a Decimal.
"""

SUBSCRIPTIONS = """\
GIVING = """\

SELECT * FROM (
SELECT DISTINCT ON (s.team)
Expand All @@ -1231,9 +1232,9 @@ def get_subscriptions_for_profile(self):
, s.ctime
, s.mtime
, t.name as team_name
FROM subscriptions s
FROM payment_instructions s
JOIN teams t ON s.team = t.slug
WHERE subscriber = %s
WHERE participant = %s
AND t.is_approved is true
AND t.is_closed is not true
ORDER BY s.team
Expand All @@ -1243,18 +1244,18 @@ def get_subscriptions_for_profile(self):
, team_slug

"""
subscriptions = self.db.all(SUBSCRIPTIONS, (self.username,))
giving = self.db.all(GIVING, (self.username,))


# Compute the total.
# ==================

total = sum([s.amount for s in subscriptions])
total = sum([rec.amount for rec in giving])
if not total:
# If tips is an empty list, total is int 0. We want a Decimal.
total = Decimal('0.00')

return subscriptions, total
return giving, total

def get_current_tips(self):
"""Get the tips this participant is currently sending to others.
Expand Down
16 changes: 8 additions & 8 deletions gratipay/models/team.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,23 +87,23 @@ def status(self):
}[self.is_approved]

def migrate_tips(self):
subscriptions = self.db.all("""
SELECT s.*
FROM subscriptions s
JOIN teams t ON t.slug = s.team
payment_instructions = self.db.all("""
SELECT pi.*
FROM payment_instructions pi
JOIN teams t ON t.slug = pi.team
JOIN participants p ON t.owner = p.username
WHERE p.username = %s
AND s.ctime < t.ctime
AND pi.ctime < t.ctime
""", (self.owner, ))

# Make sure the migration hasn't been done already
if subscriptions:
if payment_instructions:
raise AlreadyMigrated

self.db.run("""

INSERT INTO subscriptions
(ctime, mtime, subscriber, team, amount, is_funded)
INSERT INTO payment_instructions
(ctime, mtime, participant, team, amount, is_funded)
SELECT ct.ctime
, ct.mtime
, ct.tipper
Expand Down
32 changes: 16 additions & 16 deletions gratipay/utils/fake_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ def fake_team(db, teamowner):
productorservice = ['Product','Service']

teamname = faker.first_name() + fake_text_id(3)
teamslugname = faker.city()
teamslugname = faker.city()

try:
#using community.slugize
Expand All @@ -105,15 +105,15 @@ def fake_team(db, teamowner):

return Team.from_slug(teamslug)

def fake_subscription(db, subscriber, subscribee):
"""Create a fake subscription
def fake_payment_instruction(db, participant, team):
"""Create a fake payment_instruction
"""
return _fake_thing( db
, "subscriptions"
, "payment_instructions"
, ctime=faker.date_time_this_year()
, mtime=faker.date_time_this_month()
, subscriber=subscriber.username
, team=subscribee.slug
, participant=participant.username
, team=team.slug
, amount=fake_tip_amount()
)

Expand Down Expand Up @@ -272,7 +272,7 @@ def clean_db(db):
""")


def populate_db(db, num_participants=100, num_tips=200, num_teams=5, num_transfers=5000, num_communities=20):
def populate_db(db, num_participants=100, ntips=200, num_teams=5, num_transfers=5000, num_communities=20):
"""Populate DB with fake data.
"""
print("Making Participants")
Expand All @@ -286,19 +286,19 @@ def populate_db(db, num_participants=100, num_tips=200, num_teams=5, num_transfe
for teamowner in teamowners:
teams.append(fake_team(db, teamowner))

print("Making Subscriptions")
subscriptioncount = 0
print("Making Payment Instructions")
npayment_instructions = 0
for participant in participants:
for team in teams:
#eliminate self-subscription
#eliminate self-payment
if participant.username != team.owner:
subscriptioncount += 1
if subscriptioncount > num_tips:
npayment_instructions += 1
if npayment_instructions > ntips:
break
fake_subscription(db, participant, team)
if subscriptioncount > num_tips:
fake_payment_instruction(db, participant, team)
if npayment_instructions > ntips:
break


print("Making Elsewheres")
for p in participants:
Expand All @@ -318,7 +318,7 @@ def populate_db(db, num_participants=100, num_tips=200, num_teams=5, num_transfe

print("Making Tips")
tips = []
for i in xrange(num_tips):
for i in xrange(ntips):
tipper, tippee = random.sample(participants, 2)
tips.append(fake_tip(db, tipper, tippee))

Expand Down
29 changes: 29 additions & 0 deletions js/gratipay/giving.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
Gratipay.giving = {}

Gratipay.giving.init = function() {
Gratipay.giving.activateTab('active');
$('.giving #tab-nav a').on('click', Gratipay.giving.handleClick);
}

Gratipay.giving.handleClick = function(e) {
e.preventDefault();
var $target = $(e.target);
Gratipay.giving.activateTab($target.data('tab'));
}

Gratipay.giving.activateTab = function(tab) {
$.each($('.giving #tab-nav a'), function(i, obj) {
var $obj = $(obj);
if ($obj.data('tab') == tab) {
$obj.addClass('selected');
} else {
$obj.removeClass('selected');
}
})

$.each($('.giving .tab'), function(i, obj) {
var $obj = $(obj);
if ($obj.data('tab') == tab) { $obj.show(); } else { $obj.hide(); }
})
}

Loading