Repository: gratipay/gratipay.com

Issue:
Update receiving amounts when updating last_bill_result
Should fix #2495.



Discussion:
👍

Tests are passing, I rechecked everything after having slept, I don't see how this could break anything badly, so I'm going to merge.


Resolving patch:
diff --git a/gittip/billing/__init__.py b/gittip/billing/__init__.py
--- a/gittip/billing/__init__.py
+++ b/gittip/billing/__init__.py
@@ -22,14 +22,35 @@
 from aspen.utils import typecheck
 
 
-def update_receiving_amounts(db, tipper, error):
-    """Update receiving amounts of participants tipped by given tipper.
+def store_result(db, thing, username, new_result):
+    """Update the participant's last_{ach,bill}_result in the DB.
+
+    Also update receiving amounts of the participant's tippees.
     """
-    from gittip.models.participant import Participant
-    tipper = Participant.from_username(tipper)
-    if tipper.is_suspicious or tipper.last_bill_result == error:
+    assert thing in ("credit card", "bank account"), thing
+    x = "bill" if thing == "credit card" else "ach"
+
+    # Update last_thing_result in the DB
+    SQL = """
+
+        UPDATE participants p
+           SET last_{0}_result=%s
+         WHERE username=%s
+     RETURNING is_suspicious
+             , ( SELECT last_{0}_result
+                   FROM participants p2
+                  WHERE p2.id = p.id
+               ) AS old_result
+
+    """.format(x)
+    p = db.one(SQL, (new_result, username))
+
+    # Update the receiving amounts of tippees if necessary
+    if thing != "credit card":
+        return
+    if p.is_suspicious or new_result == p.old_result:
         return
-    if error == '':
+    if new_result == '':
         op = '+'
     else:
         op = '-'
@@ -42,7 +63,7 @@ def update_receiving_amounts(db, tipper, error):
                ORDER BY tippee, mtime DESC
                ) foo
          WHERE tippee = username;
-    """.format(op), dict(tipper=tipper))
+    """.format(op), dict(tipper=username))
 
 
 def get_balanced_account(db, username, balanced_customer_href):
@@ -94,19 +115,12 @@ def associate(db, thing, username, balanced_customer_href, balanced_thing_uri):
                                                , balanced_customer_href
                                                 )
     invalidate_on_balanced(thing, balanced_account.href)
-    SQL = "UPDATE participants SET last_%s_result=%%s WHERE username=%%s"
     try:
         if thing == "credit card":
-            SQL %= "bill"
             obj = balanced.Card.fetch(balanced_thing_uri)
-            #add = balanced_account.add_card
-
         else:
             assert thing == "bank account", thing # sanity check
-            SQL %= "ach"
             obj = balanced.BankAccount.fetch(balanced_thing_uri)
-            #add = balanced_account.add_bank_account
-
         obj.associate_to_customer(balanced_account)
     except balanced.exc.HTTPError as err:
         error = err.message.message.decode('UTF-8')  # XXX UTF-8?
@@ -114,9 +128,7 @@ def associate(db, thing, username, balanced_customer_href, balanced_thing_uri):
         error = ''
     typecheck(error, unicode)
 
-    db.run(SQL, (error, username))
-    if thing == "credit card":
-        update_receiving_amounts(db, username, error)
+    store_result(db, thing, username, error)
     return error
 
 
@@ -144,33 +156,8 @@ def clear(db, thing, username, balanced_customer_href):
              , username, unicode
              , balanced_customer_href, (unicode, str)
               )
-    assert thing in ("credit card", "bank account"), thing
     invalidate_on_balanced(thing, balanced_customer_href)
-    CLEAR = """\
-
-        UPDATE participants
-           SET last_%s_result=NULL
-         WHERE username=%%s
-
-    """ % ("bill" if thing == "credit card" else "ach")
-    db.run(CLEAR, (username,))
-    if thing == "credit card":
-        update_receiving_amounts(db, username, None)
-
-
-def store_error(db, thing, username, msg):
-    typecheck(thing, unicode, username, unicode, msg, unicode)
-    assert thing in ("credit card", "bank account"), thing
-    ERROR = """\
-
-        UPDATE participants
-           SET last_%s_result=%%s
-         WHERE username=%%s
-
-    """ % ("bill" if thing == "credit card" else "ach")
-    db.run(ERROR, (msg, username))
-    if thing == "credit card":
-        update_receiving_amounts(db, username, msg)
+    store_result(db, thing, username, None)
 
 
 class BalancedThing(object):


Test patch:
diff --git a/gittip/testing/balanced.py b/gittip/testing/balanced.py
--- a/gittip/testing/balanced.py
+++ b/gittip/testing/balanced.py
@@ -9,7 +9,7 @@
 class BalancedHarness(Harness):
 
     def setUp(self):
-        self.david = self.make_participant('david', elsewhere='github',
+        self.david = self.make_participant('david', claimed_time='now',
                                            balanced_customer_href=self.david_href)
         self.janet = self.make_participant('janet', is_suspicious=False,
                                            claimed_time='now',
diff --git a/tests/py/test_billing.py b/tests/py/test_billing.py
--- a/tests/py/test_billing.py
+++ b/tests/py/test_billing.py
@@ -107,6 +107,7 @@ def test_balanced_bank_has_an_account_number(self):
 class TestBillingAssociate(BalancedHarness):
 
     def test_associate_valid_card(self):
+        self.david.set_tip_to(self.homer, 10)
         card = balanced.Card(
             number='4242424242424242',
             expiration_year=2020,
@@ -124,6 +125,9 @@ def test_associate_valid_card(self):
         assert len(cards) == 1
         assert cards[0].href == card.href
 
+        homer = Participant.from_id(self.homer.id)
+        assert homer.receiving == 10
+
     def test_associate_invalid_card(self): #, find):
         billing.associate( self.db
                          , u"credit card"
@@ -195,19 +199,19 @@ def test_clear_bank_account(self):
         assert david.balanced_customer_href
 
 
-class TestBillingStoreError(Harness):
+class TestBillingStoreResult(Harness):
 
     def setUp(self):
-        super(TestBillingStoreError, self).setUp()
+        super(TestBillingStoreResult, self).setUp()
         self.make_participant('alice')
 
-    def test_store_error_stores_bill_error(self):
-        billing.store_error(self.db, u"credit card", "alice", "cheese is yummy")
+    def test_store_result_stores_bill_error(self):
+        billing.store_result(self.db, u"credit card", "alice", "cheese is yummy")
         alice = Participant.from_username('alice')
         assert alice.last_bill_result == "cheese is yummy"
 
-    def test_store_error_stores_ach_error(self):
+    def test_store_result_stores_ach_error(self):
         for message in ['cheese is yummy', 'cheese smells like my vibrams']:
-            billing.store_error(self.db, u"bank account", 'alice', message)
+            billing.store_result(self.db, u"bank account", 'alice', message)
             alice = Participant.from_username('alice')
             assert alice.last_ach_result == message