Coverage for polar/models/subscription_product_price.py: 55%
30 statements
« prev ^ index » next coverage.py v7.10.6, created at 2025-12-05 15:52 +0000
« prev ^ index » next coverage.py v7.10.6, created at 2025-12-05 15:52 +0000
1from typing import TYPE_CHECKING, Self 1ab
2from uuid import UUID 1ab
4from sqlalchemy import ForeignKey, Integer, Uuid 1ab
5from sqlalchemy.orm import Mapped, declared_attr, mapped_column, relationship 1ab
7from polar.kit.db.models import RecordModel 1ab
8from polar.models.product_price import ( 1ab
9 LegacyRecurringProductPriceCustom,
10 LegacyRecurringProductPriceFixed,
11 ProductPrice,
12 ProductPriceCustom,
13 ProductPriceFixed,
14 ProductPriceSeatUnit,
15)
17if TYPE_CHECKING: 17 ↛ 18line 17 didn't jump to line 18 because the condition on line 17 was never true1ab
18 from polar.models import Subscription
21class SubscriptionProductPrice(RecordModel): 1ab
22 __tablename__ = "subscription_product_prices" 1ab
24 subscription_id: Mapped[UUID] = mapped_column( 1ab
25 Uuid,
26 ForeignKey("subscriptions.id", ondelete="cascade"),
27 primary_key=True,
28 )
29 product_price_id: Mapped[UUID] = mapped_column( 1ab
30 Uuid,
31 ForeignKey("product_prices.id", ondelete="restrict"),
32 primary_key=True,
33 )
34 amount: Mapped[int] = mapped_column(Integer, nullable=False) 1ab
36 @declared_attr 1ab
37 def product_price(cls) -> Mapped["ProductPrice"]: 1ab
38 # This is an association table, so eager loading makes sense
39 return relationship("ProductPrice", lazy="joined") 1ab
41 @declared_attr 1ab
42 def subscription(cls) -> Mapped["Subscription"]: 1ab
43 return relationship("Subscription", lazy="raise_on_sql") 1ab
45 @classmethod 1ab
46 def from_price( 1ab
47 cls,
48 price: "ProductPrice",
49 amount: int | None = None,
50 seats: int | None = None,
51 ) -> Self:
52 if isinstance(price, ProductPriceFixed | LegacyRecurringProductPriceFixed):
53 amount = price.price_amount
54 elif isinstance(price, ProductPriceCustom | LegacyRecurringProductPriceCustom):
55 assert amount is not None, "amount must be provided for custom prices"
56 elif isinstance(price, ProductPriceSeatUnit):
57 assert seats is not None, "seats must be provided for seat-based prices"
58 amount = price.calculate_amount(seats)
59 else:
60 amount = 0
61 return cls(product_price=price, amount=amount)