diff options
| author | stuppie | 2026-09-29 10:19:33 -0600 |
|---|---|---|
| committer | stuppie | 2026-09-29 10:19:33 -0600 |
| commit | ca73e3876ee241726ef0162dd78a0c2bf35174ee (patch) | |
| tree | 0259f8ce3da7277d59b2912ad25909a08843ed1a | |
| parent | bb9eded73727d84665836c2ca30dd849d9bb7987 (diff) | |
| download | generalresearch-ca73e3876ee241726ef0162dd78a0c2bf35174ee.tar.gz generalresearch-ca73e3876ee241726ef0162dd78a0c2bf35174ee.zip | |
cashout methods : with an optional can_redeem
4 files changed, 31 insertions, 16 deletions
diff --git a/generalresearch/managers/thl/ledger_manager/thl_ledger.py b/generalresearch/managers/thl/ledger_manager/thl_ledger.py index de7cae9..cd82d4a 100644 --- a/generalresearch/managers/thl/ledger_manager/thl_ledger.py +++ b/generalresearch/managers/thl/ledger_manager/thl_ledger.py @@ -2009,6 +2009,16 @@ class ThlLedgerManager(LedgerManager): ) return self.get_account_balance(wallet) + def get_user_cashout_balance(self, user: User) -> int: + """Return the balance currently available for user cashouts.""" + wallet_balance = self.get_user_wallet_balance(user) + balance_type = user.product.user_wallet_config.balance_type + if balance_type == "wallet_balance": + return wallet_balance + if balance_type == "redeemable_balance": + return self.get_user_redeemable_wallet_balance(user, wallet_balance) + raise ValueError(f"unexpected balance_type={balance_type}") + def get_user_redeemable_wallet_balance( self, user: User, user_wallet_balance: int ) -> PositiveInt: diff --git a/generalresearch/managers/thl/wallet/user_payout.py b/generalresearch/managers/thl/wallet/user_payout.py index 432eac9..99f80ef 100644 --- a/generalresearch/managers/thl/wallet/user_payout.py +++ b/generalresearch/managers/thl/wallet/user_payout.py @@ -8,7 +8,7 @@ import slack from pydantic import NonNegativeInt, PositiveInt from redis import Redis -from generalresearch.currency import USDCent +from generalresearch.currency import USDCent, format_usd_cent from generalresearch.managers.thl.cashout_method import CashoutMethodManager from generalresearch.managers.thl.ledger_manager.exceptions import ( LedgerTransactionCreateError, @@ -106,7 +106,7 @@ class UserPayoutEventManager(PayoutEventManager): product_user_id=pe.user.product_user_id, amount=USDCent(pe.amount), payout_type=pe.payout_type, - created=pe.created + created=pe.created, ) @staticmethod @@ -430,7 +430,6 @@ class UserPayoutEventManager(PayoutEventManager): f"Amount must be between 0 and $250.00. Got {amount.to_usd_str()}" ) - product = user.product banned_countries = user.product.user_health_config.banned_countries assert not user_ip_history_manager.is_user_anonymous(user), ( @@ -439,20 +438,10 @@ class UserPayoutEventManager(PayoutEventManager): if country_iso in banned_countries: raise AssertionError("Banned country requesting redemption") - wallet_balance = ledger_manager.get_user_wallet_balance(user) - if product.user_wallet_config.balance_type == "wallet_balance": - redeemable_amount = wallet_balance - elif product.user_wallet_config.balance_type == "redeemable_balance": - redeemable_amount = ledger_manager.get_user_redeemable_wallet_balance( - user, wallet_balance - ) - else: - raise ValueError( - f"unexpected balance_type={product.user_wallet_config.balance_type}" - ) + redeemable_amount = ledger_manager.get_user_cashout_balance(user) assert amount <= redeemable_amount, ( - f"User requesting more than their redeemable balance ({amount} > {redeemable_amount})" + f"User requesting more than their redeemable balance ({amount.to_usd_str()} > {format_usd_cent(redeemable_amount)})" ) # Simple dedupe mechanism. Don't allow more than 1 per user_id per minute per cashout_method. diff --git a/generalresearch/models/thl/wallet/cashout_method.py b/generalresearch/models/thl/wallet/cashout_method.py index 5502f86..518aa2f 100644 --- a/generalresearch/models/thl/wallet/cashout_method.py +++ b/generalresearch/models/thl/wallet/cashout_method.py @@ -195,9 +195,23 @@ class CashoutMethodOut(CashoutMethodBase): "incrementing values.", ) + can_redeem: bool | None = Field( + default=None, + description="Whether the user currently has enough redeemable wallet balance " + "to meet this cashout method's minimum value. None when not evaluated for " + "a user.", + ) + @classmethod - def from_cashout_method(cls, cm: CashoutMethod) -> Self: + def from_cashout_method( + cls, + cm: CashoutMethod, + *, + cashout_balance: int | None = None, + ) -> Self: d = cm.model_dump() + if cashout_balance is not None and cm.min_value_usd is not None: + d["can_redeem"] = cashout_balance >= int(cm.min_value_usd) if cm.user: d["product_id"] = cm.user.product_id d["product_user_id"] = cm.user.product_user_id diff --git a/tests/managers/thl/test_ledger/test_wallet.py b/tests/managers/thl/test_ledger/test_wallet.py index 0a1da73..f154c19 100644 --- a/tests/managers/thl/test_ledger/test_wallet.py +++ b/tests/managers/thl/test_ledger/test_wallet.py @@ -59,6 +59,7 @@ class TestGetUserWalletBalance: user: User = user_factory(product=schrute_product) balance = thl_ledger_manager.get_user_wallet_balance(user=user) assert balance == 0 + assert thl_ledger_manager.get_user_cashout_balance(user=user) == 0 assert isinstance(user.product, Product) balance_string = user.product.format_payout_format(Decimal(balance) / 100) assert balance_string == "0 Schrute Bucks" @@ -97,3 +98,4 @@ class TestGetUserWalletBalance: user=user, user_wallet_balance=balance ) assert redeemable_balance == 20 + 100 + assert thl_ledger_manager.get_user_cashout_balance(user=user) == 20 + 100 |
