Coverage for polar/models/checkout_link.py: 88%
38 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 1ab
2from uuid import UUID 1ab
4from sqlalchemy import Boolean, ForeignKey, String, Uuid 1ab
5from sqlalchemy.ext.associationproxy import AssociationProxy, association_proxy 1ab
6from sqlalchemy.orm import Mapped, declared_attr, mapped_column, relationship 1ab
8from polar.enums import PaymentProcessor 1ab
9from polar.kit.db.models import RecordModel 1ab
10from polar.kit.metadata import MetadataMixin 1ab
11from polar.kit.trial import TrialConfigurationMixin 1ab
13from .discount import Discount 1ab
14from .product import Product 1ab
16if TYPE_CHECKING: 16 ↛ 17line 16 didn't jump to line 17 because the condition on line 16 was never true1ab
17 from .checkout_link_product import CheckoutLinkProduct
18 from .organization import Organization
21class CheckoutLink(TrialConfigurationMixin, MetadataMixin, RecordModel): 1ab
22 __tablename__ = "checkout_links" 1ab
24 payment_processor: Mapped[PaymentProcessor] = mapped_column( 1ab
25 String, nullable=False, default=PaymentProcessor.stripe, index=True
26 )
27 client_secret: Mapped[str] = mapped_column( 1ab
28 String, index=True, nullable=False, unique=True
29 )
30 _success_url: Mapped[str | None] = mapped_column( 1ab
31 "success_url", String, nullable=True, default=None
32 )
34 label: Mapped[UUID] = mapped_column(String, nullable=True) 1ab
35 allow_discount_codes: Mapped[bool] = mapped_column( 1ab
36 Boolean, nullable=False, default=True
37 )
38 require_billing_address: Mapped[bool] = mapped_column( 1ab
39 Boolean, nullable=False, default=False
40 )
42 discount_id: Mapped[UUID | None] = mapped_column( 1ab
43 Uuid, ForeignKey("discounts.id", ondelete="set null"), nullable=True
44 )
46 @declared_attr 1ab
47 def discount(cls) -> Mapped[Discount | None]: 1ab
48 # Eager loading makes sense here because we always need the discount when present
49 return relationship(Discount, lazy="joined") 1ab
51 checkout_link_products: Mapped[list["CheckoutLinkProduct"]] = relationship( 1ab
52 "CheckoutLinkProduct",
53 back_populates="checkout_link",
54 cascade="all, delete-orphan",
55 order_by="CheckoutLinkProduct.order",
56 # Products are almost always needed, so eager loading makes sense
57 lazy="selectin",
58 )
60 products: AssociationProxy[list["Product"]] = association_proxy( 1ab
61 "checkout_link_products", "product"
62 )
64 # Denormalize organization_id to help with validation
65 # when updating products or discount
66 organization_id: Mapped[UUID] = mapped_column( 1ab
67 Uuid,
68 ForeignKey("organizations.id", ondelete="cascade"),
69 nullable=False,
70 index=True,
71 )
73 @declared_attr 1ab
74 def organization(cls) -> Mapped["Organization"]: 1ab
75 return relationship("Organization", lazy="raise") 1ab
77 @property 1ab
78 def success_url(self) -> str | None: 1ab
79 return self._success_url
81 @success_url.setter 1ab
82 def success_url(self, value: str | None) -> None: 1ab
83 self._success_url = str(value) if value is not None else None