Coverage for polar/payout/repository.py: 34%
52 statements
« prev ^ index » next coverage.py v7.10.6, created at 2025-12-05 17:15 +0000
« prev ^ index » next coverage.py v7.10.6, created at 2025-12-05 17:15 +0000
1from collections.abc import Sequence 1a
2from datetime import timedelta 1a
3from uuid import UUID 1a
5from sqlalchemy import Select, false 1a
6from sqlalchemy.orm import joinedload 1a
8from polar.auth.models import AuthSubject, Organization, User, is_organization, is_user 1a
9from polar.config import settings 1a
10from polar.enums import AccountType 1a
11from polar.kit.repository import ( 1a
12 Options,
13 RepositoryBase,
14 RepositorySoftDeletionIDMixin,
15 RepositorySoftDeletionMixin,
16 RepositorySortingMixin,
17 SortingClause,
18)
19from polar.kit.utils import utc_now 1a
20from polar.models import Account, Payout, Transaction 1a
21from polar.models.payout import PayoutStatus 1a
22from polar.payout.sorting import PayoutSortProperty 1a
25class PayoutRepository( 1a
26 RepositorySoftDeletionIDMixin[Payout, UUID],
27 RepositorySoftDeletionMixin[Payout],
28 RepositorySortingMixin[Payout, PayoutSortProperty],
29 RepositoryBase[Payout],
30):
31 model = Payout 1a
32 sorting_enum = PayoutSortProperty 1a
34 async def count_by_account(self, account: UUID) -> int: 1a
35 statement = self.get_base_statement().where(Payout.account_id == account)
36 return await self.count(statement)
38 async def get_by_processor_id( 1a
39 self,
40 processor: AccountType,
41 processor_id: str,
42 *,
43 options: Options = (),
44 ) -> Payout | None:
45 statement = (
46 self.get_base_statement()
47 .where(
48 Payout.processor == processor,
49 Payout.processor_id == processor_id,
50 )
51 .options(*options)
52 )
53 return await self.get_one_or_none(statement)
55 async def get_all_stripe_pending( 1a
56 self, delay: timedelta = settings.ACCOUNT_PAYOUT_DELAY
57 ) -> Sequence[Payout]:
58 statement = (
59 self.get_base_statement()
60 .distinct(Payout.account_id)
61 .where(
62 Payout.processor == AccountType.stripe,
63 Payout.status == PayoutStatus.pending,
64 Payout.processor_id.is_(None),
65 Payout.created_at < utc_now() - delay,
66 )
67 .order_by(Payout.account_id.asc(), Payout.created_at.asc())
68 )
69 return await self.get_all(statement)
71 async def get_by_account_and_invoice_number( 1a
72 self, account: UUID, invoice_number: str
73 ) -> Payout | None:
74 statement = self.get_base_statement().where(
75 Payout.account_id == account,
76 Payout.invoice_number == invoice_number,
77 )
78 return await self.get_one_or_none(statement)
80 def get_eager_options(self) -> Options: 1a
81 return (
82 joinedload(Payout.account),
83 joinedload(Payout.transaction).selectinload(
84 Transaction.incurred_transactions
85 ),
86 )
88 def get_readable_statement( 1a
89 self, auth_subject: AuthSubject[User | Organization]
90 ) -> Select[tuple[Payout]]:
91 statement = self.get_base_statement()
93 if is_user(auth_subject):
94 user = auth_subject.subject
95 statement = statement.join(Payout.account).where(
96 Account.admin_id == user.id
97 )
98 elif is_organization(auth_subject):
99 # Only the admin of the account can access it
100 statement = statement.where(false())
102 return statement
104 def get_sorting_clause(self, property: PayoutSortProperty) -> SortingClause: 1a
105 match property:
106 case PayoutSortProperty.created_at:
107 return Payout.created_at
108 case PayoutSortProperty.amount:
109 return Payout.amount
110 case PayoutSortProperty.fees_amount:
111 return Payout.fees_amount
112 case PayoutSortProperty.status:
113 return Payout.status
114 case PayoutSortProperty.paid_at:
115 return Payout.paid_at
116 case PayoutSortProperty.account_id:
117 return Payout.account_id