Skip to content

Commit

Permalink
retry failed early renewals when they become due
Browse files Browse the repository at this point in the history
  • Loading branch information
Changaco committed Sep 1, 2024
1 parent f65d052 commit 34ca72d
Show file tree
Hide file tree
Showing 3 changed files with 27 additions and 18 deletions.
35 changes: 19 additions & 16 deletions liberapay/models/participant.py
Original file line number Diff line number Diff line change
Expand Up @@ -2730,36 +2730,41 @@ 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
AND tippee_p.status = 'active'
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("""
Expand Down Expand Up @@ -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.
Expand Down
5 changes: 5 additions & 0 deletions liberapay/models/payin_transfer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from postgres.orm import Model


class PayinTransfer(Model):
typname = "payin_transfers"
5 changes: 3 additions & 2 deletions liberapay/wireup.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down

0 comments on commit 34ca72d

Please sign in to comment.