diff --git a/liberapay/billing/fees.py b/liberapay/billing/fees.py index 7355e8cae5..cbd3934936 100644 --- a/liberapay/billing/fees.py +++ b/liberapay/billing/fees.py @@ -2,25 +2,25 @@ """ from __future__ import division, print_function, unicode_literals -from decimal import Decimal, ROUND_UP - -from pando.utils import typecheck +from mangopay.utils import Money from liberapay.constants import ( - D_CENT, PAYIN_CARD_MIN, FEE_PAYIN_CARD, FEE_PAYIN_BANK_WIRE, PAYIN_BANK_WIRE_MIN, FEE_PAYIN_DIRECT_DEBIT, PAYIN_DIRECT_DEBIT_MIN, - FEE_PAYOUT, FEE_PAYOUT_OUTSIDE_SEPA, SEPA, - FEE_VAT, + FEE_PAYOUT, + Fees, ) -def upcharge(amount, fees, min_amount): +def upcharge(amount, fees, min_amounts): """Given an amount, return a higher amount and the difference. """ - typecheck(amount, Decimal) + assert isinstance(amount, Money), type(amount) + + fees = fees if isinstance(fees, Fees) else fees[amount.currency] + min_amount = min_amounts[amount.currency] if amount < min_amount: amount = min_amount @@ -30,14 +30,14 @@ def upcharge(amount, fees, min_amount): fee = charge_amount - amount # + VAT - vat = fee * FEE_VAT + vat = fee * Fees.VAT charge_amount += vat fee += vat # Round - charge_amount = charge_amount.quantize(D_CENT, rounding=ROUND_UP) - fee = fee.quantize(D_CENT, rounding=ROUND_UP) - vat = vat.quantize(D_CENT, rounding=ROUND_UP) + charge_amount = charge_amount.round_up() + fee = fee.round_up() + vat = vat.round_up() return charge_amount, fee, vat @@ -51,10 +51,10 @@ def skim_amount(amount, fees): """Given a nominal amount, compute the fees, taxes, and the actual amount. """ fee = amount * fees.var + fees.fix - vat = fee * FEE_VAT + vat = fee * Fees.VAT fee += vat - fee = fee.quantize(D_CENT, rounding=ROUND_UP) - vat = vat.quantize(D_CENT, rounding=ROUND_UP) + fee = fee.round_up() + vat = vat.round_up() return amount - fee, fee, vat @@ -76,10 +76,15 @@ def skim_credit(amount, ba): The returned amount can be negative, look out for that. """ - typecheck(amount, Decimal) + assert isinstance(amount, Money), type(amount) + fees = FEE_PAYOUT[amount.currency] country = get_bank_account_country(ba) - if country in SEPA: - fee = FEE_PAYOUT + if 'domestic' in fees: + countries, domestic_fee = fees['domestic'] + if country in countries: + fee = domestic_fee + else: + fee = fees['foreign'] else: - fee = FEE_PAYOUT_OUTSIDE_SEPA + fee = fees['*'] return skim_amount(amount, fee) diff --git a/liberapay/billing/transactions.py b/liberapay/billing/transactions.py index 472ac5646a..5d8aad6d6e 100644 --- a/liberapay/billing/transactions.py +++ b/liberapay/billing/transactions.py @@ -10,7 +10,6 @@ SettlementTransfer, Transfer, User, Wallet, ) from mangopay.utils import Money -from pando.utils import typecheck from liberapay.billing.fees import ( skim_bank_wire, skim_credit, upcharge_card, upcharge_direct_debit @@ -84,6 +83,7 @@ def payout(db, route, amount, ignore_high_fee=False): ba = BankAccount.get(route.address, user_id=participant.mangopay_user_id) # Do final calculations + amount = Money(amount, 'EUR') if isinstance(amount, Decimal) else amount credit_amount, fee, vat = skim_credit(amount, ba) if credit_amount <= 0 and fee > 0: raise FeeExceedsAmount @@ -95,9 +95,9 @@ def payout(db, route, amount, ignore_high_fee=False): e_id = record_exchange(db, route, -credit_amount, fee, vat, participant, 'pre').id payout = BankWirePayOut() payout.AuthorId = participant.mangopay_user_id - payout.DebitedFunds = Money(int(amount * 100), 'EUR') + payout.DebitedFunds = amount.int() payout.DebitedWalletId = participant.mangopay_wallet_id - payout.Fees = Money(int(fee * 100), 'EUR') + payout.Fees = fee.int() payout.BankAccountId = route.address payout.BankWireRef = str(e_id) payout.Tag = str(e_id) @@ -119,12 +119,13 @@ def charge(db, route, amount, return_url): and add it to amount to end up with charge_amount. """ - typecheck(amount, Decimal) + assert isinstance(amount, (Decimal, Money)), type(amount) assert route assert route.network == 'mango-cc' participant = route.participant + amount = Money(amount, 'EUR') if isinstance(amount, Decimal) else amount charge_amount, fee, vat = upcharge_card(amount) amount = charge_amount - fee @@ -135,10 +136,10 @@ def charge(db, route, amount, return_url): payin = DirectPayIn() payin.AuthorId = participant.mangopay_user_id payin.CreditedWalletId = participant.mangopay_wallet_id - payin.DebitedFunds = Money(int(charge_amount * 100), 'EUR') + payin.DebitedFunds = charge_amount.int() payin.CardId = route.address payin.SecureModeReturnURL = return_url - payin.Fees = Money(int(fee * 100), 'EUR') + payin.Fees = fee.int() payin.Tag = str(e_id) try: test_hook() @@ -158,12 +159,13 @@ def charge(db, route, amount, return_url): def prepare_direct_debit(db, route, amount): """Prepare to debit a bank account. """ - typecheck(amount, Decimal) + assert isinstance(amount, (Decimal, Money)), type(amount) assert route.network == 'mango-ba' participant = route.participant + amount = Money(amount, 'EUR') if isinstance(amount, Decimal) else amount debit_amount, fee, vat = upcharge_direct_debit(amount) amount = debit_amount - fee @@ -197,16 +199,16 @@ def execute_direct_debit(db, exchange, route): assert exchange.status == 'pre' - amount, fee = exchange.amount, exchange.fee + amount, fee = Money(exchange.amount, 'EUR'), Money(exchange.fee, 'EUR') debit_amount = amount + fee e_id = exchange.id payin = DirectDebitDirectPayIn() payin.AuthorId = participant.mangopay_user_id payin.CreditedWalletId = participant.mangopay_wallet_id - payin.DebitedFunds = Money(int(debit_amount * 100), 'EUR') + payin.DebitedFunds = debit_amount.int() payin.MandateId = route.mandate - payin.Fees = Money(int(fee * 100), 'EUR') + payin.Fees = fee.int() payin.Tag = str(e_id) try: test_hook() @@ -229,6 +231,8 @@ def payin_bank_wire(db, participant, debit_amount): route = ExchangeRoute.upsert_bankwire_route(participant) + if not isinstance(debit_amount, Money): + debit_amount = Money(debit_amount, 'EUR') amount, fee, vat = skim_bank_wire(debit_amount) if not participant.mangopay_wallet_id: @@ -238,8 +242,8 @@ def payin_bank_wire(db, participant, debit_amount): payin = BankWirePayIn() payin.AuthorId = participant.mangopay_user_id payin.CreditedWalletId = participant.mangopay_wallet_id - payin.DeclaredDebitedFunds = Money(int(debit_amount * 100), 'EUR') - payin.DeclaredFees = Money(int(fee * 100), 'EUR') + payin.DeclaredDebitedFunds = debit_amount.int() + payin.DeclaredFees = fee.int() payin.Tag = str(e_id) try: test_hook() @@ -258,19 +262,20 @@ def record_unexpected_payin(db, payin): """Record an unexpected bank wire payin. """ assert payin.PaymentType == 'BANK_WIRE' - amount = Decimal(payin.DebitedFunds.Amount) / Decimal(100) - paid_fee = Decimal(payin.Fees.Amount) / Decimal(100) - vat = skim_bank_wire(amount)[2] + debited_amount = payin.DebitedFunds / Decimal(100) + paid_fee = payin.Fees / Decimal(100) + vat = skim_bank_wire(debited_amount)[2].amount wallet_id = payin.CreditedWalletId participant = Participant.from_mangopay_user_id(payin.AuthorId) assert participant.mangopay_wallet_id == wallet_id route = ExchangeRoute.upsert_bankwire_route(participant) + amount = (debited_amount - paid_fee).amount return db.one(""" INSERT INTO exchanges (amount, fee, vat, participant, status, route, note, remote_id, wallet_id) VALUES (%s, %s, %s, %s, 'created', %s, NULL, %s, %s) RETURNING id - """, (amount, paid_fee, vat, participant.id, route.id, payin.Id, wallet_id)) + """, (amount, paid_fee.amount, vat, participant.id, route.id, payin.Id, wallet_id)) def record_payout_refund(db, payout_refund): @@ -314,6 +319,10 @@ def record_exchange(db, route, amount, fee, vat, participant, status, error=None if participant.is_suspended: raise AccountSuspended() + amount = getattr(amount, 'amount', amount) + fee = getattr(fee, 'amount', fee) + vat = getattr(vat, 'amount', vat) + with db.get_cursor() as cursor: wallet_id = participant.mangopay_wallet_id diff --git a/liberapay/constants.py b/liberapay/constants.py index 2f570feb7d..fc4b5da117 100644 --- a/liberapay/constants.py +++ b/liberapay/constants.py @@ -31,7 +31,16 @@ def check_bits(bits): Event = namedtuple('Event', 'name bit title') -Fees = namedtuple('Fees', ('var', 'fix')) + +class Fees(namedtuple('Fees', ('var', 'fix'))): + VAT = Decimal('0.17') # 17% (Luxembourg rate) + VAT_1 = VAT + 1 + + @property + def with_vat(self): + r = (self.var * self.VAT_1 * 100, self.fix * self.VAT_1) + return r[0] if not r[1] else r[1] if not r[0] else r + StandardTip = namedtuple('StandardTip', 'label weekly monthly yearly') @@ -55,15 +64,18 @@ def check_bits(bits): D_UNIT = Decimal('1.00') D_ZERO = Decimal('0.00') -DONATION_LIMITS_WEEKLY = (Decimal('0.01'), Decimal('100.00')) -DONATION_LIMITS = { - 'weekly': DONATION_LIMITS_WEEKLY, +DONATION_LIMITS_WEEKLY_EUR_USD = (Decimal('0.01'), Decimal('100.00')) +DONATION_LIMITS_EUR_USD = { + 'weekly': DONATION_LIMITS_WEEKLY_EUR_USD, 'monthly': tuple((x * Decimal(52) / Decimal(12)).quantize(D_CENT, rounding=ROUND_UP) - for x in DONATION_LIMITS_WEEKLY), + for x in DONATION_LIMITS_WEEKLY_EUR_USD), 'yearly': tuple((x * Decimal(52)).quantize(D_CENT) - for x in DONATION_LIMITS_WEEKLY), + for x in DONATION_LIMITS_WEEKLY_EUR_USD), +} +DONATION_LIMITS = { + 'EUR': {k: (Money(v[0], 'EUR'), Money(v[1], 'EUR')) for k, v in DONATION_LIMITS_EUR_USD.items()}, + 'USD': {k: (Money(v[0], 'USD'), Money(v[1], 'USD')) for k, v in DONATION_LIMITS_EUR_USD.items()}, } -DONATION_WEEKLY_MIN, DONATION_WEEKLY_MAX = DONATION_LIMITS_WEEKLY DOMAIN_RE = re.compile(r''' ^ @@ -106,13 +118,33 @@ def check_bits(bits): EVENTS_S = ' '.join(EVENTS.keys()) # https://www.mangopay.com/pricing/ -FEE_PAYIN_BANK_WIRE = Fees(Decimal('0.005'), Decimal(0)) # 0.5% -FEE_PAYIN_CARD = Fees(Decimal('0.018'), Decimal('0.18')) # 1.8% + €0.18 -FEE_PAYIN_DIRECT_DEBIT = Fees(Decimal(0), Decimal('0.80')) # €0.80 -FEE_PAYOUT = Fees(Decimal(0), Decimal(0)) -FEE_PAYOUT_OUTSIDE_SEPA = Fees(Decimal(0), Decimal('2.5')) +SEPA = set(""" + AT BE BG CH CY CZ DE DK EE ES ES FI FR GB GI GR HR HU IE IS IT LI LT LU LV + MC MT NL NO PL PT RO SE SI SK +""".split()) +FEE_PAYIN_BANK_WIRE = Fees(Decimal('0.005'), 0) # 0.5% +FEE_PAYIN_CARD = { + 'EUR': Fees(Decimal('0.018'), Money('0.18', 'EUR')), # 1.8% + €0.18 + 'USD': Fees(Decimal('0.025'), Money('0.30', 'USD')), # 2.5% + $0.30 +} +FEE_PAYIN_DIRECT_DEBIT = { + 'EUR': Fees(0, Money('0.80', 'EUR')), # €0.80 + 'GBP': Fees(0, Money('0.80', 'GBP')), # £0.80 +} +FEE_PAYOUT = { + 'EUR': { + 'domestic': (SEPA, Fees(0, 0)), + 'foreign': Fees(0, Money('2.50', 'EUR')), + }, + 'GBP': { + 'domestic': ({'GB'}, Fees(0, Money('0.45', 'GBP'))), + 'foreign': Fees(0, Money('1.90', 'GBP')), + }, + 'USD': { + '*': Fees(0, Money('3.00', 'USD')), + }, +} FEE_PAYOUT_WARN = Decimal('0.03') # warn user when fee exceeds 3% -FEE_VAT = Decimal('0.17') # 17% (Luxembourg rate) INVOICE_DOC_MAX_SIZE = 5000000 INVOICE_DOCS_EXTS = ['pdf', 'jpeg', 'jpg', 'png'] @@ -142,16 +174,16 @@ def check_bits(bits): KYC_DOC_MAX_SIZE_MB = int(KYC_DOC_MAX_SIZE / 1000000) KYC_DOCS_EXTS = ['pdf', 'jpeg', 'jpg', 'gif', 'png'] KYC_DOCS_EXTS_STR = ', '.join(KYC_DOCS_EXTS) -KYC_INCOME_THRESHOLDS = ( +KYC_INCOME_THRESHOLDS = [(i, Money(a, 'EUR')) for i, a in ( (1, 18000), (2, 30000), (3, 50000), (4, 80000), (5, 120000), (6, 120000), -) -KYC_PAYIN_YEARLY_THRESHOLD = Decimal('2500') -KYC_PAYOUT_YEARLY_THRESHOLD = Decimal('1000') +)] +KYC_PAYIN_YEARLY_THRESHOLD = Money('2500', 'EUR') +KYC_PAYOUT_YEARLY_THRESHOLD = Money('1000', 'EUR') LAUNCH_TIME = datetime(2016, 2, 3, 12, 50, 0, 0, utc) @@ -164,12 +196,29 @@ def check_bits(bits): PASSWORD_MIN_SIZE = 8 PASSWORD_MAX_SIZE = 150 -PAYIN_BANK_WIRE_MIN = Decimal('2.00') # fee ≈ 0.99% -PAYIN_BANK_WIRE_TARGET = Decimal('5.00') # fee ≈ 0.6% -PAYIN_CARD_MIN = Decimal("15.00") # fee ≈ 3.5% -PAYIN_CARD_TARGET = Decimal("92.00") # fee ≈ 2.33% -PAYIN_DIRECT_DEBIT_MIN = Decimal('25.00') # fee ≈ 3.6% -PAYIN_DIRECT_DEBIT_TARGET = Decimal('99.00') # fee ≈ 0.94% +PAYIN_BANK_WIRE_MIN = {k: Money('2.00', k) for k in ('EUR', 'USD')} # fee ≈ 0.99% +PAYIN_BANK_WIRE_TARGET = {k: Money('5.00', k) for k in ('EUR', 'USD')} # fee ≈ 0.6% +PAYIN_BANK_WIRE_MAX = {k: Money('2500.00', k) for k in ('EUR', 'USD')} +PAYIN_CARD_MIN = { + 'EUR': Money('15.00', 'EUR'), # fee ≈ 3.5% + 'USD': Money('20.00', 'USD'), # fee ≈ 4.58% +} +PAYIN_CARD_TARGET = { + 'EUR': Money('92.00', 'EUR'), # fee ≈ 2.33% + 'USD': Money('95.00', 'USD'), # fee ≈ 3.27% +} +PAYIN_CARD_MAX = {k: Money('2500.00', k) for k in ('EUR', 'USD')} +PAYIN_DIRECT_DEBIT_MIN_EUR_GBP = Decimal('25.00') # fee ≈ 3.6% +PAYIN_DIRECT_DEBIT_MIN = { + 'EUR': Money(PAYIN_DIRECT_DEBIT_MIN_EUR_GBP, 'EUR'), + 'GBP': Money(PAYIN_DIRECT_DEBIT_MIN_EUR_GBP, 'GBP'), +} +PAYIN_DIRECT_DEBIT_TARGET_EUR_GBP = Decimal('99.00') # fee ≈ 0.94% +PAYIN_DIRECT_DEBIT_TARGET = { + 'EUR': Money(PAYIN_DIRECT_DEBIT_TARGET_EUR_GBP, 'EUR'), + 'GBP': Money(PAYIN_DIRECT_DEBIT_TARGET_EUR_GBP, 'GBP'), +} +PAYIN_DIRECT_DEBIT_MAX = {k: Money('2500.00', k) for k in ('EUR', 'USD')} PERIOD_CONVERSION_RATES = { 'weekly': Decimal(1), @@ -208,32 +257,31 @@ def check_bits(bits): 'sign-up.ip-version': (15, 15*60), # 15 per 15 minutes per IP version } -SEPA = set(""" - AT BE BG CH CY CZ DE DK EE ES ES FI FR GB GI GR HR HU IE IS IT LI LT LU LV - MC MT NL NO PL PT RO SE SI SK -""".split()) - SESSION = str('session') # bytes in python2, unicode in python3 SESSION_REFRESH = timedelta(hours=1) SESSION_TIMEOUT = timedelta(hours=6) -def make_standard_tip(label, weekly): +def make_standard_tip(label, weekly, currency): return StandardTip( label, - weekly, - weekly / PERIOD_CONVERSION_RATES['monthly'], - weekly / PERIOD_CONVERSION_RATES['yearly'], + Money(weekly, currency), + Money(weekly / PERIOD_CONVERSION_RATES['monthly'], currency), + Money(weekly / PERIOD_CONVERSION_RATES['yearly'], currency), ) -STANDARD_TIPS = ( - make_standard_tip(_("Symbolic"), Decimal('0.01')), - make_standard_tip(_("Small"), Decimal('0.25')), - make_standard_tip(_("Medium"), Decimal('1.00')), - make_standard_tip(_("Large"), Decimal('5.00')), - make_standard_tip(_("Maximum"), DONATION_WEEKLY_MAX), +STANDARD_TIPS_EUR_USD = ( + (_("Symbolic"), Decimal('0.01')), + (_("Small"), Decimal('0.25')), + (_("Medium"), Decimal('1.00')), + (_("Large"), Decimal('5.00')), + (_("Maximum"), DONATION_LIMITS_EUR_USD['weekly'][1]), ) +STANDARD_TIPS = { + 'EUR': [make_standard_tip(label, weekly, 'EUR') for label, weekly in STANDARD_TIPS_EUR_USD], + 'USD': [make_standard_tip(label, weekly, 'USD') for label, weekly in STANDARD_TIPS_EUR_USD], +} SUMMARY_MAX_SIZE = 100 diff --git a/liberapay/exceptions.py b/liberapay/exceptions.py index 11e74a662d..54ffeab8ea 100644 --- a/liberapay/exceptions.py +++ b/liberapay/exceptions.py @@ -3,7 +3,7 @@ from dependency_injection import resolve_dependencies from pando import Response -from .constants import DONATION_LIMITS, PASSWORD_MIN_SIZE, PASSWORD_MAX_SIZE +from .constants import PASSWORD_MIN_SIZE, PASSWORD_MAX_SIZE class Redirect(Exception): @@ -202,14 +202,24 @@ def msg(self, _): class BadAmount(LazyResponse400): def msg(self, _): - period = self.args[1] - return _(BAD_AMOUNT_MESSAGES[period], self.args[0], *DONATION_LIMITS[period]) + amount, period, limits = self.args + return _(BAD_AMOUNT_MESSAGES[period], amount, *limits) class UserDoesntAcceptTips(LazyResponseXXX): code = 403 def msg(self, _): return _("The user {0} doesn't accept donations.", *self.args) +class BadDonationCurrency(LazyResponseXXX): + code = 403 + def msg(self, _): + tippee, rejected_currency = self.args + return _( + "Donations to {username} must be in {main_currency}, not {rejected_currency}.", + username=tippee.username, main_currency=tippee.main_currency, + rejected_currency=rejected_currency, + ) + class NonexistingElsewhere(LazyResponse400): def msg(self, _): diff --git a/liberapay/models/participant.py b/liberapay/models/participant.py index b830b12a7a..eac8355272 100644 --- a/liberapay/models/participant.py +++ b/liberapay/models/participant.py @@ -1,7 +1,7 @@ from __future__ import print_function, unicode_literals from base64 import b64decode, b64encode -from decimal import Decimal, ROUND_DOWN, ROUND_UP +from decimal import ROUND_DOWN, ROUND_UP from email.utils import formataddr from hashlib import pbkdf2_hmac, md5 from os import urandom @@ -13,6 +13,7 @@ import aspen_jinja2_renderer from html2text import html2text import mangopay +from mangopay.utils import Money from markupsafe import escape as htmlescape from pando.utils import utcnow from postgres.orm import Model @@ -21,13 +22,14 @@ from liberapay.constants import ( ASCII_ALLOWED_IN_USERNAME, AVATAR_QUERY, D_CENT, D_ZERO, - DONATION_WEEKLY_MAX, DONATION_WEEKLY_MIN, EMAIL_RE, + DONATION_LIMITS, EMAIL_RE, EMAIL_VERIFICATION_TIMEOUT, EVENTS, PASSWORD_MAX_SIZE, PASSWORD_MIN_SIZE, PERIOD_CONVERSION_RATES, PRIVILEGES, SESSION, SESSION_REFRESH, SESSION_TIMEOUT, USERNAME_MAX_SIZE ) from liberapay.exceptions import ( BadAmount, + BadDonationCurrency, BadEmailAddress, BadPasswordSize, CannotRemovePrimaryEmail, @@ -540,7 +542,7 @@ def clear_tips_giving(self, cursor): """, (self.id,)) for tippee in tippees: - self.set_tip_to(tippee, '0.00', update_self=False, cursor=cursor) + self.set_tip_to(tippee, Money(0, 'EUR'), update_self=False, cursor=cursor) def clear_tips_receiving(self, cursor): """Zero out tips to a given user. @@ -557,7 +559,7 @@ def clear_tips_receiving(self, cursor): """, (self.id,)) for tipper in tippers: - tipper.set_tip_to(self, '0.00', update_tippee=False, cursor=cursor) + tipper.set_tip_to(self, Money(0, 'EUR'), update_tippee=False, cursor=cursor) def clear_takes(self, cursor): """Leave all teams by zeroing all takes. @@ -1559,16 +1561,20 @@ def set_tip_to(self, tippee, periodic_amount, period='weekly', if self.id == tippee.id: raise NoSelfTipping - periodic_amount = Decimal(periodic_amount) # May raise InvalidOperation amount = periodic_amount * PERIOD_CONVERSION_RATES[period] - if periodic_amount != 0 and amount < DONATION_WEEKLY_MIN or amount > DONATION_WEEKLY_MAX: - raise BadAmount(periodic_amount, period) + if periodic_amount != 0: + limits = DONATION_LIMITS[amount.currency]['weekly'] + if amount < limits[0] or amount > limits[1]: + raise BadAmount(periodic_amount, period, limits) - amount = amount.quantize(D_CENT, rounding=ROUND_UP) + amount = amount.round_up() - if not tippee.accepts_tips and amount != 0: - raise UserDoesntAcceptTips(tippee.username) + if amount != 0: + if not tippee.accepts_tips: + raise UserDoesntAcceptTips(tippee.username) + if not tippee.accept_all_currencies and amount.currency != tippee.main_currency: + raise BadDonationCurrency(tippee, amount.currency) # Insert tip t = (cursor or self.db).one("""\ @@ -1586,8 +1592,8 @@ def set_tip_to(self, tippee, periodic_amount, period='weekly', , ( SELECT count(*) = 0 FROM tips WHERE tipper=%(tipper)s ) AS first_time_tipper , ( SELECT join_time IS NULL FROM participants WHERE id = %(tippee)s ) AS is_pledge - """, dict(tipper=self.id, tippee=tippee.id, amount=amount, - period=period, periodic_amount=periodic_amount))._asdict() + """, dict(tipper=self.id, tippee=tippee.id, amount=amount.amount, + period=period, periodic_amount=periodic_amount.amount))._asdict() if update_self: # Update giving amount of tipper diff --git a/liberapay/testing/__init__.py b/liberapay/testing/__init__.py index ba6ae608e2..ca53cb9267 100644 --- a/liberapay/testing/__init__.py +++ b/liberapay/testing/__init__.py @@ -8,6 +8,7 @@ from os.path import dirname, join, realpath from aspen import resources +from mangopay.utils import Money from pando.utils import utcnow from pando.testing.client import Client from psycopg2 import IntegrityError, InternalError @@ -31,6 +32,10 @@ PROJECT_ROOT = str(TOP) +def EUR(amount): + return Money(amount, 'EUR') + + class ClientWithAuth(Client): def __init__(self, *a, **kw): diff --git a/liberapay/utils/fake_data.py b/liberapay/utils/fake_data.py index 9216734584..e5585912aa 100644 --- a/liberapay/utils/fake_data.py +++ b/liberapay/utils/fake_data.py @@ -90,7 +90,8 @@ def fake_tip(db, tipper, tippee): """Create a fake tip. """ period = random.choice(DONATION_PERIODS) - periodic_amount = random_money_amount(*DONATION_LIMITS[period]) + limits = [l.amount for l in DONATION_LIMITS['EUR'][period]] + periodic_amount = random_money_amount(*limits) amount = (periodic_amount * PERIOD_CONVERSION_RATES[period]).quantize(D_CENT) return _fake_thing( db, @@ -222,7 +223,7 @@ def populate_db(website, num_participants=100, num_tips=200, num_teams=5, num_tr tips.append(fake_tip(db, tipper, tippee)) # Transfers - min_amount, max_amount = DONATION_LIMITS['weekly'] + min_amount, max_amount = [l.amount for l in DONATION_LIMITS['EUR']['weekly']] transfers = [] for i in range(num_transfers): tipper, tippee = random.sample(participants, 2) diff --git a/templates/identity-form-2.html b/templates/identity-form-2.html index f233e30620..f30e1ff0ea 100644 --- a/templates/identity-form-2.html +++ b/templates/identity-form-2.html @@ -54,7 +54,6 @@ % set saved = account.IncomeRange % set prev_threshold = None % for i, threshold in constants.KYC_INCOME_THRESHOLDS - % set threshold = Money(threshold, 'EUR')