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

Commit

Permalink
Refined and added tests
Browse files Browse the repository at this point in the history
  • Loading branch information
rorepo authored and chadwhitacre committed Aug 19, 2015
1 parent fe2c70f commit 1b3a373
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 55 deletions.
43 changes: 8 additions & 35 deletions gratipay/billing/payday.py
Original file line number Diff line number Diff line change
Expand Up @@ -236,12 +236,14 @@ def f(p):
# not up to minimum charge level. cancel the hold
cancel_card_hold(holds.pop(p.id))
return
hold, error = create_card_hold(self.db, p, amount)
if error:
return 1
else:
holds[p.id] = hold
if amount >= MINIMUM_CHARGE:
hold, error = create_card_hold(self.db, p, amount)
if error:
return 1
else:
holds[p.id] = hold
n_failures = sum(filter(None, threaded_map(f, participants)))


# Record the number of failures
cursor.one("""
Expand Down Expand Up @@ -302,36 +304,6 @@ def park_payment_instructions(cursor):
""")


@staticmethod
def park_payment_instructions(cursor):
"""In the case of participants in whose case the amount to be charged to their cc's
in order to meet all outstanding and current subscriptions does not reach the minimum
charge threshold, park this for the next week by adding the current subscription amount
to giving_due
"""
cursor.run("""
INSERT INTO participants_payments_uncharged
SELECT participant
FROM payday_payment_instructions
GROUP BY participant
HAVING SUM(amount + giving_due) < %(MINIMUM_CHARGE)s
""", dict(MINIMUM_CHARGE=MINIMUM_CHARGE))

cursor.run("""
UPDATE payment_instructions
SET giving_due = amount + giving_due
WHERE id IN ( SELECT id
FROM payday_payment_instructions ppi
, participants_payments_uncharged ppu
WHERE ppi.participant = ppu.participant
)
""")


@staticmethod
def transfer_takes(cursor, ts_start):
return # XXX Bring me back!
Expand Down Expand Up @@ -426,6 +398,7 @@ def update_balances(cursor):
FROM payday_payment_instructions pi2
WHERE pi.id = pi2.id
""")

log("Updated the balances of %i participants." % len(participants))


Expand Down
23 changes: 20 additions & 3 deletions sql/payday.sql
Original file line number Diff line number Diff line change
Expand Up @@ -86,16 +86,16 @@ UPDATE payday_payment_instructions ppi

DROP TABLE IF EXISTS participants_payments_uncharged;
CREATE TABLE participants_payments_uncharged AS
SELECT participant
SELECT id, giving_due
FROM payday_payment_instructions
WHERE 1 = 2;

ALTER TABLE payday_participants ADD COLUMN giving_today numeric(35,2);
UPDATE payday_participants
UPDATE payday_participants pp
SET giving_today = COALESCE((
SELECT sum(amount + giving_due)
FROM payday_payment_instructions
WHERE participant = username
WHERE participant = pp.username
), 0);

DROP TABLE IF EXISTS payday_takes;
Expand Down Expand Up @@ -160,6 +160,20 @@ RETURNS void AS $$
END;
$$ LANGUAGE plpgsql;

-- Add payments that were not met on to giving_due

CREATE OR REPLACE FUNCTION park(text, text, numeric)
RETURNS void AS $$
BEGIN
IF ($3 = 0) THEN RETURN; END IF;

UPDATE payday_payment_instructions
SET giving_due = $3
WHERE participant = $1
AND team = $2;
END;
$$ LANGUAGE plpgsql;


-- Create a trigger to process payment_instructions

Expand All @@ -175,6 +189,9 @@ CREATE OR REPLACE FUNCTION process_payment_instruction() RETURNS trigger AS $$
IF (NEW.amount + NEW.giving_due <= participant.new_balance OR participant.card_hold_ok) THEN
EXECUTE pay(NEW.participant, NEW.team, NEW.amount + NEW.giving_due, 'to-team');
RETURN NEW;
ELSE
EXECUTE park(NEW.participant, NEW.team, NEW.amount + NEW.giving_due);
RETURN NULL;
END IF;
RETURN NULL;
END;
Expand Down
40 changes: 30 additions & 10 deletions tests/py/test_billing_payday.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import mock
import pytest

from gratipay.billing.exchanges import create_card_hold
from gratipay.billing.exchanges import create_card_hold, MINIMUM_CHARGE
from gratipay.billing.payday import NoPayday, Payday
from gratipay.exceptions import NegativeBalance
from gratipay.models.participant import Participant
Expand All @@ -19,18 +19,38 @@

class TestPayday(BillingHarness):

def test_payday_moves_money(self):
def test_payday_moves_money_above_min_charge(self):
A = self.make_team(is_approved=True)
self.obama.set_payment_instruction(A, '6.00') # under $10!
self.obama.set_payment_instruction(A, MINIMUM_CHARGE) # must be >= MINIMUM_CHARGE
with mock.patch.object(Payday, 'fetch_card_holds') as fch:
fch.return_value = {}
Payday.start().run()

obama = Participant.from_username('obama')
hannibal = Participant.from_username('hannibal')

assert hannibal.balance == D(MINIMUM_CHARGE)
assert obama.balance == D('0.00')

@mock.patch.object(Payday, 'fetch_card_holds')
def test_payday_does_not_move_money_below_min_charge(self, fch):
A = self.make_team(is_approved=True)
self.obama.set_payment_instruction(A, '6.00') # not enough to reach MINIMUM_CHARGE
fch.return_value = {}
Payday.start().run()

obama = Participant.from_username('obama')
hannibal = Participant.from_username('hannibal')
giving_due = self.db.one("""SELECT giving_due
FROM payment_instructions ppi
WHERE ppi.participant = 'obama'
AND ppi.team = 'TheATeam'
""")

assert hannibal.balance == D('0.00')
assert obama.balance == D('0.00')
assert giving_due == D('6.00')

assert hannibal.balance == D('6.00')
assert obama.balance == D('3.41')

@mock.patch.object(Payday, 'fetch_card_holds')
def test_payday_doesnt_move_money_from_a_suspicious_account(self, fch):
Expand All @@ -40,7 +60,7 @@ def test_payday_doesnt_move_money_from_a_suspicious_account(self, fch):
WHERE username = 'obama'
""")
team = self.make_team(owner=self.homer, is_approved=True)
self.obama.set_payment_instruction(team, '6.00') # under $10!
self.obama.set_payment_instruction(team, MINIMUM_CHARGE) # >= MINIMUM_CHARGE!
fch.return_value = {}
Payday.start().run()

Expand All @@ -58,7 +78,7 @@ def test_payday_doesnt_move_money_to_a_suspicious_account(self, fch):
WHERE username = 'homer'
""")
team = self.make_team(owner=self.homer, is_approved=True)
self.obama.set_payment_instruction(team, '6.00') # under $10!
self.obama.set_payment_instruction(team, MINIMUM_CHARGE) # >= MINIMUM_CHARGE!
fch.return_value = {}
Payday.start().run()

Expand Down Expand Up @@ -247,10 +267,10 @@ def create_card_holds(self):
def test_payin_pays_in(self, sale, sfs, fch):
fch.return_value = {}
team = self.make_team('Gratiteam', is_approved=True)
self.obama.set_payment_instruction(team, 1)
self.obama.set_payment_instruction(team, MINIMUM_CHARGE) # >= MINIMUM_CHARGE

txn_attrs = {
'amount': 1,
'amount': MINIMUM_CHARGE,
'tax_amount': 0,
'status': 'authorized',
'custom_fields': {'participant_id': self.obama.id},
Expand All @@ -268,7 +288,7 @@ def test_payin_pays_in(self, sale, sfs, fch):

Payday.start().payin()
payments = self.db.all("SELECT amount, direction FROM payments")
assert payments == [(1, 'to-team'), (1, 'to-participant')]
assert payments == [(MINIMUM_CHARGE, 'to-team'), (MINIMUM_CHARGE, 'to-participant')]

@mock.patch('braintree.Transaction.sale')
def test_payin_doesnt_try_failed_cards(self, sale):
Expand Down
15 changes: 8 additions & 7 deletions tests/py/test_history.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from datetime import datetime
from decimal import Decimal as D
import json
import time

from mock import patch

Expand Down Expand Up @@ -40,9 +41,8 @@ class TestHistory(BillingHarness):

def test_iter_payday_events(self):
Payday().start().run()

A = self.make_team(is_approved=True)
self.obama.set_payment_instruction(A, '6.00') # under $10!
self.obama.set_payment_instruction(A, '10.00') # >= MINIMUM_CHARGE!
for i in range(2):
with patch.object(Payday, 'fetch_card_holds') as fch:
fch.return_value = {}
Expand All @@ -57,27 +57,28 @@ def test_iter_payday_events(self):
SET timestamp = "timestamp" - interval '1 week';
""")


obama = Participant.from_username('obama')
hannibal = Participant.from_username('hannibal')

assert obama.balance == D('6.82')
assert hannibal.balance == D('12.00')
assert obama.balance == D('0.00')
assert hannibal.balance == D('20.00')

Payday().start() # to demonstrate that we ignore any open payday?

events = list(iter_payday_events(self.db, hannibal))
assert len(events) == 7
assert events[0]['kind'] == 'totals'
assert events[0]['given'] == 0
assert events[0]['received'] == 12
assert events[0]['received'] == 20
assert events[1]['kind'] == 'day-open'
assert events[1]['payday_number'] == 2
assert events[2]['balance'] == 12
assert events[2]['balance'] == 20
assert events[-1]['kind'] == 'day-close'
assert events[-1]['balance'] == 0

events = list(iter_payday_events(self.db, obama))
assert events[0]['given'] == 12
assert events[0]['given'] == 20
assert len(events) == 11

def test_iter_payday_events_with_failed_exchanges(self):
Expand Down

0 comments on commit 1b3a373

Please sign in to comment.