Coverage for polar/held_balance/service.py: 51%

37 statements  

« prev     ^ index     » next       coverage.py v7.10.6, created at 2025-12-05 17:15 +0000

1import structlog 1a

2from sqlalchemy import or_, select 1a

3from sqlalchemy.orm import joinedload 1a

4 

5from polar.config import settings 1a

6from polar.exceptions import PolarError 1a

7from polar.kit.services import ResourceServiceReader 1a

8from polar.logging import Logger 1a

9from polar.models import ( 1a

10 Account, 

11 HeldBalance, 

12 Transaction, 

13) 

14from polar.models.organization import Organization 1a

15from polar.postgres import AsyncSession 1a

16from polar.transaction.service.balance import ( 1a

17 balance_transaction as balance_transaction_service, 

18) 

19from polar.transaction.service.dispute import ( 1a

20 dispute_transaction as dispute_transaction_service, 

21) 

22from polar.transaction.service.platform_fee import ( 1a

23 platform_fee_transaction as platform_fee_transaction_service, 

24) 

25from polar.transaction.service.refund import ( 1a

26 refund_transaction as refund_transaction_service, 

27) 

28 

29log: Logger = structlog.get_logger() 1a

30 

31 

32class HeldBalanceError(PolarError): ... 1a

33 

34 

35class HeldBalanceService(ResourceServiceReader[HeldBalance]): 1a

36 async def create( 1a

37 self, session: AsyncSession, *, held_balance: HeldBalance 

38 ) -> HeldBalance: 

39 session.add(held_balance) 

40 await session.flush() 

41 return held_balance 

42 

43 async def release_account( 1a

44 self, session: AsyncSession, account: Account 

45 ) -> list[tuple[Transaction, Transaction]]: 

46 statement = ( 

47 select(HeldBalance) 

48 .join( 

49 Organization, 

50 onclause=HeldBalance.organization_id == Organization.id, 

51 isouter=True, 

52 ) 

53 .where( 

54 or_( 

55 HeldBalance.account_id == account.id, 

56 Organization.account_id == account.id, 

57 ), 

58 HeldBalance.deleted_at.is_(None), 

59 ) 

60 .options( 

61 joinedload(HeldBalance.payment_transaction), 

62 joinedload(HeldBalance.pledge), 

63 joinedload(HeldBalance.order), 

64 joinedload(HeldBalance.issue_reward), 

65 ) 

66 ) 

67 held_balances = await session.stream_scalars( 

68 statement, 

69 execution_options={"yield_per": settings.DATABASE_STREAM_YIELD_PER}, 

70 ) 

71 

72 balance_transactions_list: list[tuple[Transaction, Transaction]] = [] 

73 async for held_balance in held_balances: 

74 balance_transactions = await balance_transaction_service.create_balance( 

75 session, 

76 source_account=None, 

77 destination_account=account, 

78 payment_transaction=held_balance.payment_transaction, 

79 amount=held_balance.amount, 

80 pledge=held_balance.pledge, 

81 order=held_balance.order, 

82 issue_reward=held_balance.issue_reward, 

83 ) 

84 balance_transactions_list.append(balance_transactions) 

85 

86 platform_fee_transactions = ( 

87 await platform_fee_transaction_service.create_fees_reversal_balances( 

88 session, balance_transactions=balance_transactions 

89 ) 

90 ) 

91 if held_balance.order: 

92 held_balance.order.platform_fee_amount = sum( 

93 incoming.amount for _, incoming in platform_fee_transactions 

94 ) 

95 session.add(held_balance.order) 

96 

97 await refund_transaction_service.create_reversal_balances_for_payment( 

98 session, payment_transaction=held_balance.payment_transaction 

99 ) 

100 await dispute_transaction_service.create_reversal_balances_for_payment( 

101 session, payment_transaction=held_balance.payment_transaction 

102 ) 

103 

104 await session.delete(held_balance) 

105 

106 return balance_transactions_list 

107 

108 

109held_balance = HeldBalanceService(HeldBalance) 1a