Exemple #1
0
 def test_schedule_renewals_notifies_payer_of_changes(self):
     alice = self.make_participant('alice', email='*****@*****.**')
     bob = self.make_participant('bob', email='*****@*****.**')
     alice.set_tip_to(bob, EUR('1.00'), renewal_mode=2)
     alice_card = self.upsert_route(alice,
                                    'stripe-card',
                                    address='pm_card_visa')
     self.make_payin_and_transfer(alice_card, bob, EUR('2.00'))
     new_schedule = alice.schedule_renewals()
     next_payday = compute_next_payday_date()
     expected_transfers = [{
         'tippee_id': bob.id,
         'tippee_username': '******',
         'amount': EUR('2.00'),
     }]
     assert len(new_schedule) == 1
     assert new_schedule[0].amount == EUR('2.00')
     assert new_schedule[0].transfers == expected_transfers
     expected_renewal_date = next_payday + timedelta(weeks=1)
     assert new_schedule[0].execution_date == expected_renewal_date
     assert new_schedule[0].automatic is True
     # Trigger the initial "upcoming charge" notification
     self.db.run(
         "UPDATE scheduled_payins SET ctime = ctime - interval '12 hours'")
     send_upcoming_debit_notifications()
     emails = self.get_emails()
     assert len(emails) == 1
     assert emails[0]['to'][0] == 'alice <*****@*****.**>'
     assert emails[0][
         'subject'] == 'Liberapay donation renewal: upcoming debit of €2.00'
     sp = self.db.one("SELECT * FROM scheduled_payins")
     assert sp.notifs_count == 1
     # Tweak the donation amount. The renewal shouldn't be pushed back and
     # the payer shouldn't be notified.
     alice.set_tip_to(bob, EUR('0.50'))
     new_schedule = alice.schedule_renewals()
     assert len(new_schedule) == 1
     assert new_schedule[0].amount == EUR('2.00')
     assert new_schedule[0].transfers == expected_transfers
     assert new_schedule[0].execution_date == expected_renewal_date
     assert new_schedule[0].automatic is True
     emails = self.get_emails()
     assert not emails
     # Lower the donation amount a lot more. This time the renewal should be
     # rescheduled and the payer should be notified of that change.
     alice.set_tip_to(bob, EUR('0.10'))
     new_schedule = alice.schedule_renewals()
     assert len(new_schedule) == 1
     assert new_schedule[0].amount == EUR('2.00')
     assert new_schedule[0].transfers == expected_transfers
     expected_renewal_date = next_payday + timedelta(weeks=19)
     assert new_schedule[0].execution_date == expected_renewal_date
     assert new_schedule[0].automatic is True
     emails = self.get_emails()
     assert len(emails) == 1
     assert emails[0]['to'][0] == 'alice <*****@*****.**>'
     assert emails[0][
         'subject'] == 'Liberapay donation renewal: your upcoming payment has changed'
 def test_schedule_renewals_handles_change_of_customized_renewal_to_automatic(
         self):
     alice = self.make_participant('alice')
     bob = self.make_participant('bob')
     alice.set_tip_to(bob, EUR('0.99'))
     alice_card = self.upsert_route(alice, 'stripe-card')
     self.make_payin_and_transfer(alice_card, bob, EUR('49.50'))
     new_schedule = alice.schedule_renewals()
     next_payday = compute_next_payday_date()
     expected_transfers = [{
         'tippee_id': bob.id,
         'tippee_username': '******',
         'amount': None,
     }]
     assert len(new_schedule) == 1
     assert new_schedule[0].amount is None
     assert new_schedule[0].transfers == expected_transfers
     assert new_schedule[0].execution_date == (next_payday +
                                               timedelta(weeks=50))
     assert new_schedule[0].automatic is False
     # customize the renewal
     schedule = self.db.all(
         "SELECT * FROM scheduled_payins WHERE payin IS NULL")
     assert len(schedule) == 1
     sp = schedule[0]
     r = self.client.GET("/alice/giving/schedule", auth_as=alice)
     assert r.code == 200
     r = self.client.GET("/alice/giving/schedule?id=%i&action=modify" %
                         sp.id,
                         auth_as=alice)
     assert r.code == 200
     new_date = sp.execution_date - timedelta(days=14)
     r = self.client.PxST("/alice/giving/schedule?id=%i&action=modify" %
                          sp.id, {'new_date': new_date.isoformat()},
                          auth_as=alice)
     assert r.code == 302
     sp = self.db.one("SELECT * FROM scheduled_payins WHERE id = %s",
                      (sp.id, ))
     assert sp.amount is None
     assert sp.transfers == expected_transfers
     assert sp.execution_date == new_date
     assert sp.customized is True
     new_schedule = alice.schedule_renewals()
     assert len(new_schedule) == 1
     assert new_schedule[0].amount is None
     assert new_schedule[0].transfers == expected_transfers
     assert new_schedule[0].execution_date == new_date
     assert new_schedule[0].customized is True
     # enable automatic renewal for this donation
     alice.set_tip_to(bob, EUR('0.99'), renewal_mode=2)
     expected_transfers[0]['amount'] = EUR('49.50')
     new_schedule = alice.schedule_renewals()
     assert len(new_schedule) == 1
     assert new_schedule[0].amount == EUR('49.50')
     assert new_schedule[0].transfers == expected_transfers
     assert new_schedule[0].execution_date == new_date
     assert new_schedule[0].automatic is True
Exemple #3
0
 def test_schedule_renewals(self):
     alice = self.make_participant('alice')
     bob = self.make_participant('bob')
     # pre-test
     new_schedule = alice.schedule_renewals()
     assert new_schedule == []
     # set up a donation
     alice.set_tip_to(bob, EUR('0.99'))
     new_schedule = alice.schedule_renewals()
     assert new_schedule == []
     # fund the donation
     alice_card = self.upsert_route(alice, 'stripe-card')
     self.make_payin_and_transfer(alice_card, bob, EUR('49.50'))
     new_schedule = alice.schedule_renewals()
     next_payday = compute_next_payday_date()
     expected_transfers = [{
         'tippee_id': bob.id,
         'tippee_username': '******',
         'amount': None,
     }]
     assert len(new_schedule) == 1
     assert new_schedule[0].amount is None
     assert new_schedule[0].transfers == expected_transfers
     assert new_schedule[0].execution_date == (next_payday +
                                               timedelta(weeks=49))
     assert new_schedule[0].automatic is False
     # check idempotency
     new_schedule2 = alice.schedule_renewals()
     assert len(new_schedule2) == 1
     assert new_schedule2[0].__dict__ == new_schedule[0].__dict__
     # modify the donation amount
     alice.set_tip_to(bob, EUR('1.98'))
     new_schedule = alice.schedule_renewals()
     assert len(new_schedule) == 1
     assert new_schedule[0].amount is None
     assert new_schedule[0].transfers == expected_transfers
     assert new_schedule[0].execution_date == (next_payday +
                                               timedelta(weeks=24))
     assert new_schedule[0].automatic is False
     # enable automatic renewal for this donation
     alice.set_tip_to(bob, EUR('0.99'), renewal_mode=2)
     expected_transfers[0]['amount'] = EUR('49.50')
     new_schedule = alice.schedule_renewals()
     assert len(new_schedule) == 1
     assert new_schedule[0].amount == EUR('49.50')
     assert new_schedule[0].transfers == expected_transfers
     assert new_schedule[0].execution_date == (next_payday +
                                               timedelta(weeks=49))
     assert new_schedule[0].automatic is True
Exemple #4
0
 def test_schedule_renewals_finds_partial_matches(self):
     """
     This test is designed to hit the `find_partial_match` function inside
     `Participant.schedule_renewals`.
     """
     alice = self.make_participant('alice', email='*****@*****.**')
     bob = self.make_participant('bob', email='*****@*****.**')
     team = self.make_participant('team',
                                  kind='group',
                                  email='*****@*****.**')
     team.add_member(bob)
     alice.set_tip_to(bob, EUR('1.00'), renewal_mode=2)
     alice.set_tip_to(team, EUR('1.50'), renewal_mode=2)
     alice_card = self.upsert_route(alice,
                                    'stripe-card',
                                    address='pm_card_visa')
     self.make_payin_and_transfer(alice_card, bob, EUR('2.00'))
     self.make_payin_and_transfer(alice_card, team, EUR('3.00'))
     new_schedule = alice.schedule_renewals()
     next_payday = compute_next_payday_date()
     expected_transfers = [{
         'tippee_id': bob.id,
         'tippee_username': '******',
         'amount': EUR('2.00'),
     }, {
         'tippee_id': team.id,
         'tippee_username': '******',
         'amount': EUR('3.00'),
     }]
     assert len(new_schedule) == 1
     assert new_schedule[0].amount == EUR('5.00')
     assert new_schedule[0].transfers == expected_transfers
     expected_renewal_date = next_payday + timedelta(weeks=1)
     assert new_schedule[0].execution_date == expected_renewal_date
     assert new_schedule[0].automatic is True
     # Trigger the initial "upcoming charge" notification
     self.db.run(
         "UPDATE scheduled_payins SET ctime = ctime - interval '12 hours'")
     send_upcoming_debit_notifications()
     emails = self.get_emails()
     assert len(emails) == 1
     assert emails[0]['to'][0] == 'alice <*****@*****.**>'
     assert emails[0][
         'subject'] == 'Liberapay donation renewal: upcoming debit of €5.00'
     sp = self.db.one("SELECT * FROM scheduled_payins WHERE payin IS NULL")
     assert sp.notifs_count == 1
     # Tweak the amount of the first donation. The renewal shouldn't be
     # pushed back and the payer shouldn't be notified.
     alice.set_tip_to(bob, EUR('0.50'))
     new_schedule = alice.schedule_renewals()
     assert len(new_schedule) == 1
     assert new_schedule[0].amount == EUR('5.00')
     assert new_schedule[0].transfers == expected_transfers
     assert new_schedule[0].execution_date == expected_renewal_date
     assert new_schedule[0].automatic is True
     emails = self.get_emails()
     assert not emails
     # Stop the second donation. The renewal should be rescheduled and the
     # payer should be notified of that change.
     alice.stop_tip_to(team)
     new_schedule = alice.schedule_renewals()
     assert len(new_schedule) == 1
     assert new_schedule[0].amount == EUR('2.00')
     assert new_schedule[0].transfers == [expected_transfers[0]]
     previous_renewal_date = expected_renewal_date
     expected_renewal_date = next_payday + timedelta(weeks=3)
     assert new_schedule[0].execution_date == expected_renewal_date
     assert new_schedule[0].automatic is True
     emails = self.get_emails()
     assert len(emails) == 1
     assert emails[0]['to'][0] == 'alice <*****@*****.**>'
     assert emails[0][
         'subject'] == 'Liberapay donation renewal: your upcoming payment has changed'
     expected_sentence = LOCALE_EN.format(
         "The payment of €5.00 scheduled for {old_date} has been replaced by "
         "a payment of €2.00 on {new_date}.",
         old_date=previous_renewal_date,
         new_date=expected_renewal_date)
     assert expected_sentence in emails[0]['text']
Exemple #5
0
    def test_schedule_renewals_handles_currency_switch(self):
        alice = self.make_participant('alice', email='*****@*****.**')
        bob = self.make_participant('bob', email='*****@*****.**', accepted_currencies=None)
        alice.set_tip_to(bob, EUR('1.00'), renewal_mode=2)
        alice_card = self.upsert_route(alice, 'stripe-card', address='pm_card_visa')
        self.make_payin_and_transfer(alice_card, bob, EUR('37.00'))
        schedule = self.db.all("SELECT * FROM scheduled_payins WHERE payin IS NULL")
        next_payday = compute_next_payday_date()
        expected_transfers = [
            {
                'tippee_id': bob.id,
                'tippee_username': '******',
                'amount': EUR('37.00').for_json(),
            }
        ]
        assert len(schedule) == 1
        assert schedule[0].amount == EUR('37.00')
        assert schedule[0].transfers == expected_transfers
        expected_renewal_date = next_payday + timedelta(weeks=37)
        assert schedule[0].execution_date == expected_renewal_date
        assert schedule[0].automatic is True

        # Change the currency
        alice.set_tip_to(bob, USD('1.20'), renewal_mode=2)
        schedule = self.db.all("SELECT * FROM scheduled_payins WHERE payin IS NULL")
        assert len(schedule) == 1
        expected_transfers = [dict(expected_transfers[0], amount=USD('44.40').for_json())]
        assert schedule[0].amount == USD('44.40')
        assert schedule[0].transfers == expected_transfers
        assert schedule[0].execution_date == expected_renewal_date
        assert schedule[0].automatic is True

        # Customize the renewal
        sp_id = schedule[0].id
        r = self.client.GET("/alice/giving/schedule", auth_as=alice)
        assert r.code == 200
        r = self.client.GET(
            "/alice/giving/schedule?id=%i&action=modify" % sp_id,
            auth_as=alice
        )
        assert r.code == 200
        new_date = expected_renewal_date - timedelta(days=14)
        r = self.client.PxST(
            "/alice/giving/schedule?id=%i&action=modify" % sp_id,
            {
                'amount': '42.00', 'currency': 'USD',
                'new_date': new_date.isoformat(),
            },
            auth_as=alice
        )
        assert r.code == 302
        sp = self.db.one("SELECT * FROM scheduled_payins WHERE id = %s", (sp_id,))
        assert sp.amount == USD('42.00')
        assert sp.execution_date == new_date
        assert sp.customized is True
        schedule = alice.schedule_renewals()
        assert len(schedule) == 1
        assert schedule[0].amount == USD('42.00')
        assert schedule[0].execution_date == new_date
        assert schedule[0].customized is True

        # Change the currency again
        alice.set_tip_to(bob, EUR('1.00'), renewal_mode=2)
        schedule = self.db.all("SELECT * FROM scheduled_payins WHERE payin IS NULL")
        assert len(schedule) == 1
        expected_transfers = [dict(expected_transfers[0], amount=EUR('35.00').for_json())]
        assert schedule[0].amount == EUR('35.00')
        assert schedule[0].transfers == expected_transfers
        assert schedule[0].execution_date == new_date
        assert schedule[0].automatic is True
        assert schedule[0].customized is True