From f65d05272379fdbe53490ad7be5815f7dc351b42 Mon Sep 17 00:00:00 2001 From: Changaco Date: Sun, 1 Sep 2024 09:24:07 +0200 Subject: [PATCH 1/2] fix error when attempting to change avatar source --- www/%username/edit/avatar.spt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/%username/edit/avatar.spt b/www/%username/edit/avatar.spt index b4e7115ae..6b1ace4a0 100644 --- a/www/%username/edit/avatar.spt +++ b/www/%username/edit/avatar.spt @@ -9,7 +9,7 @@ if request.method == 'POST': if src not in constants.AVATAR_SOURCES: raise response.invalid_input(src, 'src', 'body') - if '@' not in email: + if email and '@' not in email: raise BadEmailAddress(email) new_avatar_url = participant.update_avatar(src+':', avatar_email=email) From 34ca72df40955076be3e1acef89673fc49bf307b Mon Sep 17 00:00:00 2001 From: Changaco Date: Sun, 1 Sep 2024 12:01:22 +0200 Subject: [PATCH 2/2] retry failed early renewals when they become due --- liberapay/models/participant.py | 35 ++++++++++++++++-------------- liberapay/models/payin_transfer.py | 5 +++++ liberapay/wireup.py | 5 +++-- 3 files changed, 27 insertions(+), 18 deletions(-) create mode 100644 liberapay/models/payin_transfer.py diff --git a/liberapay/models/participant.py b/liberapay/models/participant.py index 0f5732a73..9bb198442 100644 --- a/liberapay/models/participant.py +++ b/liberapay/models/participant.py @@ -2730,10 +2730,19 @@ def find_partial_match(new_sp, current_schedule_map): (self.id,)) # Get renewable tips + next_payday = compute_next_payday_date() renewable_tips = cursor.all(""" - SELECT t.*::tips, tippee_p + SELECT t.*::tips, tippee_p, last_pt::payin_transfers FROM current_tips t JOIN participants tippee_p ON tippee_p.id = t.tippee + LEFT JOIN LATERAL ( + SELECT pt.* + FROM payin_transfers pt + WHERE pt.payer = t.tipper + AND coalesce(pt.team, pt.recipient) = t.tippee + ORDER BY pt.ctime DESC + LIMIT 1 + ) last_pt ON true WHERE t.tipper = %s AND t.renewal_mode > 0 AND t.paid_in_advance IS NOT NULL @@ -2741,25 +2750,21 @@ def find_partial_match(new_sp, current_schedule_map): AND ( tippee_p.goal IS NULL OR tippee_p.goal >= 0 ) AND tippee_p.is_suspended IS NOT TRUE AND tippee_p.payment_providers > 0 - AND NOT EXISTS ( - SELECT 1 - FROM ( SELECT pt.* - FROM payin_transfers pt - WHERE pt.payer = t.tipper - AND coalesce(pt.team, pt.recipient) = t.tippee - ORDER BY pt.ctime DESC - LIMIT 1 - ) pt - WHERE pt.status IN ('pending', 'failed') - ) ORDER BY t.tippee """, (self.id,)) - for tip, tippee_p in renewable_tips: + for tip, tippee_p, last_pt in renewable_tips: tip.tippee_p = tippee_p tip.periodic_amount = tip.periodic_amount.convert_if_currency_is_phased_out() tip.amount = tip.amount.convert_if_currency_is_phased_out() tip.paid_in_advance = tip.paid_in_advance.convert_if_currency_is_phased_out() - renewable_tips = [tip for tip, tippee_p in renewable_tips] + tip.due_date = tip.compute_renewal_due_date(next_payday, cursor) + renewable_tips = [ + tip for tip, tippee_p, last_pt in renewable_tips + if last_pt.id is None or + last_pt.status == 'succeeded' or + last_pt.status == 'failed' and + last_pt.ctime.date() < (tip.due_date - timedelta(weeks=1)) + ] # Get the existing schedule current_schedule = cursor.all(""" @@ -2864,10 +2869,8 @@ def find_partial_match(new_sp, current_schedule_map): # Group the tips into payments # 1) Group the tips by renewal_mode and currency. - next_payday = compute_next_payday_date() naive_tip_groups = defaultdict(list) for tip in renewable_tips: - tip.due_date = tip.compute_renewal_due_date(next_payday, cursor) naive_tip_groups[(tip.renewal_mode, tip.amount.currency)].append(tip) del renewable_tips # 2) Subgroup by payment processor and geography. diff --git a/liberapay/models/payin_transfer.py b/liberapay/models/payin_transfer.py new file mode 100644 index 000000000..d52e1ccde --- /dev/null +++ b/liberapay/models/payin_transfer.py @@ -0,0 +1,5 @@ +from postgres.orm import Model + + +class PayinTransfer(Model): + typname = "payin_transfers" diff --git a/liberapay/wireup.py b/liberapay/wireup.py index f7d765805..30496061b 100644 --- a/liberapay/wireup.py +++ b/liberapay/wireup.py @@ -42,6 +42,7 @@ from liberapay.models.exchange_route import ExchangeRoute from liberapay.models.participant import Participant from liberapay.models.payin import Payin +from liberapay.models.payin_transfer import PayinTransfer from liberapay.models.repository import Repository from liberapay.models.tip import Tip from liberapay.security.crypto import Cryptograph @@ -117,8 +118,8 @@ def back_as_Object(cols, vals): db.back_as_registry[Object] = db.back_as_registry['Object'] = back_as_Object models = ( - _AccountElsewhere, AccountElsewhere, _Community, Community, - Encrypted, ExchangeRoute, Participant, Payin, Repository, Tip, + _AccountElsewhere, AccountElsewhere, _Community, Community, Encrypted, + ExchangeRoute, Participant, Payin, PayinTransfer, Repository, Tip, ) for model in models: db.register_model(model)