def test_stt_doesnt_allow_just_any_ole_amount(self):
        alice = self.make_participant('alice')
        bob = self.make_participant('bob')

        with self.assertRaises(BadAmount) as cm:
            alice.set_tip_to(bob, EUR('0.001'))
        expected = "'€0.00' is not a valid weekly donation amount (min=€0.01, max=€100.00)"
        actual = cm.exception.render_in_english()
        assert actual == expected

        with self.assertRaises(BadAmount) as cm:
            alice.set_tip_to(bob, USD('1000.00'))
        expected = "'$1,000.00' is not a valid weekly donation amount (min=$0.01, max=$100.00)"
        actual = cm.exception.render_in_english()
        assert actual == expected

        with self.assertRaises(BadAmount) as cm:
            alice.set_tip_to(bob, USD('0.01'), 'yearly')
        expected = "'$0.01' is not a valid yearly donation amount (min=$0.52, max=$5,200.00)"
        actual = cm.exception.render_in_english()
        assert actual == expected

        with self.assertRaises(BadAmount) as cm:
            alice.set_tip_to(bob, EUR('10000'), 'yearly')
        expected = "'€10,000.00' is not a valid yearly donation amount (min=€0.52, max=€5,200.00)"
        actual = cm.exception.render_in_english()
        assert actual == expected
Example #2
0
 def test_payout_amount_under_minimum(self, gba):
     usd_user = self.make_participant('usd_user', main_currency='USD')
     route = ExchangeRoute.insert(usd_user, 'mango-ba', 'fake ID')
     self.make_exchange('mango-cc', USD(8), 0, usd_user)
     gba.return_value = self.bank_account_outside_sepa
     with self.assertRaises(FeeExceedsAmount):
         payout(self.db, route, USD('0.10'))
 def test_stripe_direct_debit_receipt(self):
     self.donor.set_tip_to(self.recipient, USD('0.99'))
     route = self.upsert_route(self.donor,
                               'stripe-sdd',
                               address='src_1E42IaFk4eGpfLOCUau5nIdg')
     payin = self.make_payin_and_transfer(route, self.recipient,
                                          USD('20.02'))[0]
     r = self.client.GET(self.donor.path('receipts/direct/%s' % payin.id),
                         auth_as=self.donor)
     assert r.code == 200, r.text
Example #4
0
 def set_up_team_with_two_currencies(self):
     team = self.team = self.make_participant(
         'team', kind='group', accepted_currencies='EUR,USD'
     )
     self.alice = self.make_participant('alice', main_currency='EUR',
                                        accepted_currencies='EUR,USD')
     team.set_take_for(self.alice, EUR('1.00'), team)
     self.bob = self.make_participant('bob', main_currency='USD',
                                      accepted_currencies='EUR,USD')
     team.set_take_for(self.bob, EUR('1.00'), team)
     self.donor1_eur = self.make_participant('donor1_eur', balance=EUR(100))
     self.donor2_usd = self.make_participant('donor2_usd', balance=USD(100))
     self.donor3_eur = self.make_participant('donor3_eur', balance=EUR(100))
     self.donor4_usd = self.make_participant('donor4_usd', balance=USD(100))
    def test_stt_converts_monthly_and_yearly_amounts_correctly(self):
        alice = self.make_participant('alice')
        bob = self.make_participant('bob', accepted_currencies='EUR,USD')

        t = alice.set_tip_to(bob, EUR('0.05'), 'monthly')
        assert t['amount'] == EUR('0.01')

        t = alice.set_tip_to(bob, USD('433.34'), 'monthly')
        assert t['amount'] == USD('100.00')

        t = alice.set_tip_to(bob, USD('0.52'), 'yearly')
        assert t['amount'] == USD('0.01')

        t = alice.set_tip_to(bob, EUR('5200.00'), 'yearly')
        assert t['amount'] == EUR('100.00')
 def test_donation_form_v2(self):
     creator = self.make_participant('creator', accepted_currencies=None)
     r = self.client.GET('/creator/donate?currency=KRW')
     assert r.code == 200
     assert ">Pledge<" in r.text
     assert ' name="currency" value="KRW"' in r.text, r.text
     self.add_payment_account(creator, 'stripe')
     r = self.client.GET('/creator/donate?currency=JPY&amount=2000&period=monthly')
     assert r.code == 200
     assert ">Donate<" in r.text
     assert ' value="2,000"'
     assert ' name="period" value="monthly" checked'
     donor = self.make_participant('donor')
     r = self.client.PxST(
         '/creator/tip',
         {
             'currency': 'USD',
             'selected_amount': '1.00',
             'renewal_mode': '2',
             'visibility': '3',
         },
         auth_as=donor,
     )
     assert r.code == 302
     assert r.headers[b'Location'] == (b'/donor/giving/pay/?beneficiary=%i' % creator.id)
     tip = donor.get_tip_to(creator)
     assert tip.amount == USD('1.00')
     assert tip.renewal_mode == 2
     assert tip.visibility == 3
Example #7
0
 def test_get_end_of_period_balances(self):
     make_history(self)
     today = get_start_of_current_utc_day()
     period_end = today.replace(month=1, day=1)
     balances = get_end_of_period_balances(self.db, self.alice, period_end,
                                           today)
     assert list(balances) == [EUR('10.00'), USD('0.00')]
Example #8
0
    def test_take_paid_in_advance_in_unaccepted_currency(self):
        team = self.make_participant('team', kind='group', accepted_currencies=None)
        alice = self.make_participant('alice', main_currency='EUR',
                                      accepted_currencies='EUR,USD')
        team.set_take_for(alice, EUR('1.00'), team)
        bob = self.make_participant('bob', main_currency='USD',
                                    accepted_currencies='EUR,USD')
        team.set_take_for(bob, USD('1.00'), team)

        stripe_account_alice = self.add_payment_account(alice, 'stripe', default_currency='EUR')
        self.add_payment_account(bob, 'stripe', country='US', default_currency='USD')

        carl = self.make_participant('carl')
        carl.set_tip_to(team, JPY('1250'))

        carl_card = ExchangeRoute.insert(
            carl, 'stripe-card', 'x', 'chargeable', remote_user_id='x'
        )
        payin, pt = self.make_payin_and_transfer(carl_card, team, JPY('1250'))
        assert pt.destination == stripe_account_alice.pk

        Payday.start().run()

        transfers = self.db.all("SELECT * FROM transfers ORDER BY id")
        assert len(transfers) == 1
        assert transfers[0].virtual is True
        assert transfers[0].tipper == carl.id
        assert transfers[0].tippee == alice.id
        assert transfers[0].amount == JPY('125')
Example #9
0
 def test_int_to_Money(self):
     expected = USD('1.02')
     actual = int_to_Money(102, 'USD')
     assert expected == actual
     expected = JPY('1')
     actual = int_to_Money(1, 'JPY')
     assert expected == actual
Example #10
0
 def test_sums(self):
     # Empty sum
     actual = self.db.one(
         "SELECT sum(x) FROM unnest(NULL::currency_amount[]) x")
     assert actual is None
     actual = self.db.one(
         "SELECT sum(x) FROM unnest(ARRAY[NULL]::currency_amount[]) x")
     assert actual is None
     # Empty fuzzy sum
     actual = self.db.one(
         "SELECT sum(x, 'EUR') FROM unnest(NULL::currency_amount[]) x")
     assert actual is None
     actual = self.db.one(
         "SELECT sum(x, 'EUR') FROM unnest(ARRAY[NULL]::currency_amount[]) x"
     )
     assert actual is None
     # Single-currency sum
     amounts = [JPY('133'), JPY('977')]
     expected = sum(amounts)
     actual = self.db.one("SELECT sum(x, 'JPY') FROM unnest(%s) x",
                          (amounts + [None], ))
     assert expected == actual
     # Fuzzy sum
     amounts = [EUR('0.50'), USD('1.20')]
     expected = MoneyBasket(*amounts).fuzzy_sum('EUR')
     actual = self.db.one("SELECT sum(x, 'EUR') FROM unnest(%s) x",
                          (amounts + [None], ))
     assert expected == actual, (expected.__dict__, actual.__dict__)
Example #11
0
    def test_payin_pages_when_currencies_dont_match(self):
        self.add_payment_account(self.creator_1, 'stripe')
        self.add_payment_account(self.creator_2, 'paypal')
        self.add_payment_account(self.creator_3, 'stripe')
        self.add_payment_account(self.creator_3, 'paypal')
        self.donor.set_tip_to(self.creator_1, EUR('11.00'))
        self.donor.set_tip_to(self.creator_2, JPY('1100'))
        self.donor.set_tip_to(self.creator_3, USD('11.00'))

        paypal_path = '/donor/giving/pay/paypal/?beneficiary=%i,%i' % (
            self.creator_2.id, self.creator_3.id
        )
        stripe_path = '/donor/giving/pay/stripe/?beneficiary=%i,%i&method=card' % (
            self.creator_1.id, self.creator_3.id
        )
        r = self.client.GET('/donor/giving/pay/', auth_as=self.donor)
        assert r.code == 200, r.text
        assert str(Markup.escape(paypal_path)) not in r.text
        assert str(Markup.escape(stripe_path)) not in r.text

        r = self.client.GxT(paypal_path, auth_as=self.donor)
        assert r.code == 400, r.text

        r = self.client.GxT(stripe_path, auth_as=self.donor)
        assert r.code == 400, r.text
Example #12
0
 def test_convert(self):
     original = EUR('1.00')
     expected = USD('1.20')
     actual = self.db.one("SELECT convert(%s, %s)", (original, expected.currency))
     assert expected == actual
     actual = original.convert(expected.currency)
     assert expected == actual
Example #13
0
    def test_underfunded_team_with_two_balanced_currencies(self):
        self.set_up_team_with_two_currencies()
        self.donor1_eur.set_tip_to(self.team, EUR('0.25'))
        self.donor2_usd.set_tip_to(self.team, USD('0.30'))
        self.donor3_eur.set_tip_to(self.team, EUR('0.25'))
        self.donor4_usd.set_tip_to(self.team, USD('0.30'))

        Payday.start().shuffle()

        expected = {
            'alice': MoneyBasket(EUR('0.50')),
            'bob': MoneyBasket(USD('0.60')),
            'donor1_eur': MoneyBasket(EUR('99.75')),
            'donor2_usd': MoneyBasket(USD('99.70')),
            'donor3_eur': MoneyBasket(EUR('99.75')),
            'donor4_usd': MoneyBasket(USD('99.70')),
        }
        actual = self.get_balances()
        assert expected == actual
Example #14
0
    def test_transfer_takes_with_two_currencies_on_both_sides(self):
        self.set_up_team_with_two_currencies()
        self.team.set_take_for(self.alice, USD('0.01'), self.alice)
        self.team.set_take_for(self.bob, EUR('0.01'), self.bob)
        self.donor1_eur.set_tip_to(self.team, EUR('0.01'))
        self.donor2_usd.set_tip_to(self.team, USD('0.01'))

        Payday.start().shuffle()

        expected = {
            'alice': MoneyBasket(EUR('0.01')),
            'bob': MoneyBasket(USD('0.01')),
            'donor1_eur': MoneyBasket(EUR('99.99')),
            'donor2_usd': MoneyBasket(USD('99.99')),
            'donor3_eur': MoneyBasket(EUR('100')),
            'donor4_usd': MoneyBasket(USD('100')),
        }
        actual = self.get_balances()
        assert expected == actual
 def test_donation_form_v2_does_not_overwrite_visibility(self):
     creator = self.make_participant('creator', accepted_currencies=None)
     self.add_payment_account(creator, 'stripe')
     donor = self.make_participant('donor')
     donor.set_tip_to(creator, USD('10.00'), renewal_mode=1, visibility=3)
     r = self.client.PxST(
         '/creator/tip',
         {
             'currency': 'USD',
             'selected_amount': '1.00',
             'renewal_mode': '2',
         },
         auth_as=donor,
     )
     assert r.code == 302
     assert r.headers[b'Location'].startswith(b'/donor/giving/')
     tip = donor.get_tip_to(creator)
     assert tip.amount == USD('1.00')
     assert tip.renewal_mode == 2
     assert tip.visibility == 3
Example #16
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
Example #17
0
 def test_sorting(self):
     amounts = [JPY('130'), EUR('99.58'), Money('79', 'KRW'), USD('35.52')]
     expected = sorted(amounts, key=lambda m: -m.convert('EUR').amount)
     actual = self.db.all("SELECT x FROM unnest(%s) x ORDER BY x DESC",
                          (amounts, ))
     assert expected == actual
Example #18
0
    def test_takes_paid_in_advance_to_now_inactive_members(self):
        team = self.make_participant('team', kind='group', accepted_currencies=None)
        alice = self.make_participant('alice', main_currency='EUR', accepted_currencies=None)
        team.set_take_for(alice, EUR('1.00'), team)
        bob = self.make_participant('bob', main_currency='USD', accepted_currencies=None)
        team.set_take_for(bob, USD('1.00'), team)

        stripe_account_alice = self.add_payment_account(
            alice, 'stripe', default_currency='EUR'
        )
        stripe_account_bob = self.add_payment_account(
            bob, 'stripe', country='US', default_currency='USD'
        )

        carl = self.make_participant('carl')
        carl.set_tip_to(team, JPY('250'))

        carl_card = ExchangeRoute.insert(
            carl, 'stripe-card', 'x', 'chargeable', remote_user_id='x'
        )
        payin, pt = self.make_payin_and_transfer(carl_card, team, JPY('1250'))
        assert pt.destination == stripe_account_alice.pk
        payin, pt = self.make_payin_and_transfer(carl_card, team, JPY('1250'))
        assert pt.destination == stripe_account_bob.pk

        team.set_take_for(alice, EUR('0.00'), team)
        team.set_take_for(bob, None, team)
        takes = dict(self.db.all("""
            SELECT DISTINCT ON (member)
                   member, paid_in_advance
              FROM takes
          ORDER BY member, mtime DESC
        """))
        assert takes == {
            alice.id: EUR('10.00'),
            bob.id: USD('12.00'),
        }

        Payday.start().run()

        transfers = self.db.all("SELECT * FROM transfers ORDER BY id")
        assert len(transfers) == 2
        assert transfers[0].virtual is True
        assert transfers[0].tipper == carl.id
        assert transfers[0].tippee == alice.id
        assert transfers[0].amount == JPY('125')
        assert transfers[1].virtual is True
        assert transfers[1].tipper == carl.id
        assert transfers[1].tippee == bob.id
        assert transfers[1].amount == JPY('125')

        takes = dict(self.db.all("""
            SELECT DISTINCT ON (member)
                   member, paid_in_advance
              FROM takes
          ORDER BY member, mtime DESC
        """))
        assert takes == {
            alice.id: EUR('9.00'),
            bob.id: USD('10.80'),
        }

        notifications = self.db.all("SELECT * FROM notifications")
        assert len(notifications) == 0

        leftovers = dict(self.db.all("SELECT username, leftover FROM participants"))
        assert leftovers == {
            'team': MoneyBasket(JPY('250.00')),
            'alice': None,
            'bob': None,
            'carl': None,
        }
Example #19
0
 def test_can_change_take_currency(self):
     team, alice, bob = self.make_team_of_two()
     self.take_last_week(team, alice, EUR('30.00'))
     team.set_take_for(alice, USD('20.00'), alice)
     self.take_last_week(team, bob, USD('42.00'))
     team.set_take_for(bob, EUR('70.00'), bob)
Example #20
0
    def test_swap_currencies(self, TR_save):
        TR_save.side_effect = fake_transfer

        self.make_exchange('mango-cc', EUR('10.00'), 0, self.janet)
        self.make_exchange('mango-cc', USD('7.00'), 0, self.homer)
        start_balances = {
            'janet': EUR('10.00'),
            'homer': USD('7.00'),
            'david': MoneyBasket(),
        }
        balances = self.get_balances()
        assert balances == start_balances

        # Test failure when there isn't enough money in the 1st wallet
        with self.assertRaises(AssertionError):
            swap_currencies(self.db, self.janet, self.homer, EUR('100.00'), USD('120.00'))
        balances = self.get_balances()
        assert balances == start_balances

        # Test failure when there isn't enough money in the 2nd wallet
        with self.assertRaises(AssertionError):
            swap_currencies(self.db, self.janet, self.homer, EUR('10.00'), USD('12.00'))
        balances = self.get_balances()
        assert balances == start_balances

        # Test failure of the 1st `prepare_transfer()` call
        with patch('liberapay.billing.transactions.lock_bundles') as lock_bundles:
            lock_bundles.side_effect = NegativeBalance
            with self.assertRaises(NegativeBalance):
                swap_currencies(self.db, self.janet, self.homer, EUR('3.00'), USD('3.00'))
        balances = self.get_balances()
        assert balances == start_balances

        # Test failure of the 2nd `prepare_transfer()` call
        cash_bundle = self.db.one("SELECT * FROM cash_bundles WHERE owner = %s", (self.homer.id,))
        self.db.run("UPDATE cash_bundles SET amount = %s WHERE id = %s",
                    (USD('0.01'), cash_bundle.id))
        with self.assertRaises(NegativeBalance):
            swap_currencies(self.db, self.janet, self.homer, EUR('5.00'), USD('6.99'))
        self.db.run("UPDATE cash_bundles SET amount = %s WHERE id = %s",
                    (cash_bundle.amount, cash_bundle.id))
        balances = self.get_balances()
        assert balances == start_balances

        # Test failure of the 1st `initiate_transfer()` call
        self.transfer_mock.side_effect = Foobar
        with self.assertRaises(TransferError):
            swap_currencies(self.db, self.janet, self.homer, EUR('4.25'), USD('5.55'))
        balances = self.get_balances()
        assert balances == start_balances

        # Test failure of the 2nd `initiate_transfer()` call
        def fail_on_second(tr):
            if getattr(fail_on_second, 'called', False):
                raise Foobar
            fail_on_second.called = True
            fake_transfer(tr)
        self.transfer_mock.side_effect = fail_on_second
        self.db.run("ALTER SEQUENCE transfers_id_seq RESTART WITH 1")
        with patch('mangopay.resources.Transfer.get') as T_get:
            T_get.return_value = Transfer(Id=-1, AuthorId=self.janet_id, Tag='1')
            with self.assertRaises(TransferError):
                swap_currencies(self.db, self.janet, self.homer, EUR('0.01'), USD('0.01'))
        balances = self.get_balances()
        assert balances == start_balances

        # Test success
        self.transfer_mock.side_effect = fake_transfer
        swap_currencies(self.db, self.janet, self.homer, EUR('5.00'), USD('6.00'))
        balances = self.get_balances()
        assert balances == {
            'janet': MoneyBasket(EUR('5.00'), USD('6.00')),
            'homer': MoneyBasket(EUR('5.00'), USD('1.00')),
            'david': MoneyBasket(),
        }
 def test_get_end_of_year_balances(self):
     make_history(self)
     balances = get_end_of_year_balances(self.db, self.alice, self.past_year, datetime.now().year)
     assert list(balances) == [EUR('10.00'), USD('0.00')]