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

implement a flag for paying out Gratipay 1.0 balances #3535

Merged
merged 8 commits into from
Jun 11, 2015
Merged
Show file tree
Hide file tree
Changes from 4 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
13 changes: 7 additions & 6 deletions bin/masspay.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,12 +132,13 @@ def compute_input_csv():
---- Only include team owners
---- TODO: Include members on payroll once process_payroll is implemented

AND ( SELECT count(*)
FROM teams t
WHERE t.owner = p.username
AND t.is_approved IS TRUE
AND t.is_closed IS NOT TRUE
) > 0
AND (( SELECT count(*)
FROM teams t
WHERE t.owner = p.username
AND t.is_approved IS TRUE
AND t.is_closed IS NOT TRUE
) > 0
OR p.status_of_1_0_balance='pending-payday')

ORDER BY p.balance DESC

Expand Down
17 changes: 8 additions & 9 deletions gratipay/billing/payday.py
Original file line number Diff line number Diff line change
Expand Up @@ -408,23 +408,22 @@ def payout(self):
log("Starting payout loop.")
participants = self.db.all("""
SELECT p.*::participants
FROM participants p
WHERE balance > 0
AND ( SELECT count(*)
FROM exchange_routes r
WHERE r.participant = p.id
AND network = 'balanced-ba'
) > 0
FROM exchange_routes r
JOIN participants p ON p.id = r.participant
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If a user had 4 old bank accounts and 1 active one, wouldn't this result in multiple calls to ach_credit with the same participant? Maybe changing this to current_exchange_routes would solve the problem?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does the same bug afflict MassPay?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it does - that's a serious bug, we could end up paying people double their balance :/ Here (ach payouts in payday), the worst case scenario is a few extra API calls.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we could end up paying people double their balance

SELECT COUNT(*)
  FROM participants p
 WHERE (
 SELECT COUNT(*)
   FROM exchange_routes r
  WHERE network='paypal'
    AND r.participant = p.id
) > 1; -- returns 0

Hasn't happened till now.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think replacing exchange_routes with current_exchange_routes in both places should solve the problem.

WHERE r.network = 'balanced-ba'
AND p.balance > 0

---- Only include team owners
---- TODO: Include members on payroll once process_payroll is implemented

AND ( SELECT count(*)
AND (( SELECT count(*)
FROM teams t
WHERE t.owner = p.username
AND t.is_approved IS TRUE
AND t.is_closed IS NOT TRUE
) > 0
) > 0
OR p.status_of_1_0_balance='pending-payout')

""")
def credit(participant):
if participant.is_suspicious is None:
Expand Down
26 changes: 26 additions & 0 deletions sql/branch.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
BEGIN;

CREATE TYPE status_of_1_0_balance AS ENUM
('unresolved', 'pending-payout', 'resolved');

ALTER TABLE participants
ADD COLUMN status_of_1_0_balance status_of_1_0_balance
NOT NULL
DEFAULT 'unresolved';

CREATE FUNCTION set_status_of_1_0_balance_to_resolved() RETURNS trigger AS $$
BEGIN
UPDATE participants
SET status_of_1_0_balance='resolved'
WHERE id = NEW.id;
RETURN NULL;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER update_status_of_1_0_balance
AFTER UPDATE OF balance ON participants
FOR EACH ROW
WHEN (OLD.balance > 0 AND NEW.balance = 0)
EXECUTE PROCEDURE set_status_of_1_0_balance_to_resolved();

END;
36 changes: 36 additions & 0 deletions tests/py/test_billing_payday.py
Original file line number Diff line number Diff line change
Expand Up @@ -564,6 +564,42 @@ def test_payout_ach_error_gets_recorded(self, ach_credit):
payday = self.fetch_payday()
assert payday['nach_failing'] == 1

@mock.patch('gratipay.billing.payday.ach_credit')
def test_payout_pays_out_Gratipay_1_0_balance(self, ach):
alice = self.make_participant('alice', claimed_time='now', is_suspicious=False,
balanced_customer_href='foo', last_ach_result='',
balance=20, status_of_1_0_balance='pending-payout')
Payday.start().payout()

assert ach.call_count == 1
assert ach.call_args_list[0][0][1].id == alice.id
assert ach.call_args_list[0][0][2] == 0

@mock.patch('balanced.BankAccount.credit')
def test_paying_out_sets_1_0_status_to_resolved(self, credit):
alice = self.make_participant('alice', claimed_time='now', is_suspicious=False,
balanced_customer_href='foo', last_ach_result='',
balance=0, status_of_1_0_balance='pending-payout')
self.make_exchange('balanced-cc', 20, 0, alice) # sets balance, and satisfies self_check
Payday.start().payout()
alice = Participant.from_username('alice')
assert alice.status_of_1_0_balance == 'resolved'
assert alice.balance == 0

@mock.patch('balanced.BankAccount.credit')
def test_payout_ignores_unresolved(self, credit):
bob = self.make_participant('bob', claimed_time='now', is_suspicious=False,
balanced_customer_href='foo', last_ach_result='',
balance=13, status_of_1_0_balance='unresolved')
alice = self.make_participant('alice', claimed_time='now', is_suspicious=False,
balanced_customer_href='foo', last_ach_result='',
balance=0, status_of_1_0_balance='pending-payout')
self.make_exchange('balanced-cc', 20, 0, alice)
Payday.start().payout()
bob = Participant.from_username('bob')
assert bob.status_of_1_0_balance == 'unresolved'
assert bob.balance == 13


class TestNotifyParticipants(EmailHarness):

Expand Down