Coverage for polar/models/benefit.py: 83%
50 statements
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-07 12:42 +0000
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-07 12:42 +0000
1from enum import StrEnum
2from typing import TYPE_CHECKING
3from uuid import UUID
5from sqlalchemy import Boolean, ForeignKey, Text, Uuid
6from sqlalchemy.dialects.postgresql import JSONB
7from sqlalchemy.orm import Mapped, declared_attr, mapped_column, relationship
9from polar.exceptions import PolarError
10from polar.kit.db.models import RecordModel
11from polar.kit.extensions.sqlalchemy.types import StringEnum
12from polar.kit.metadata import MetadataMixin
14if TYPE_CHECKING: 14 ↛ 15line 14 didn't jump to line 15 because the condition on line 14 was never true
15 from polar.benefit.strategies import BenefitProperties
16 from polar.models import BenefitGrant, Organization
19class TaxApplicationMustBeSpecified(PolarError):
20 def __init__(self, type: "BenefitType") -> None:
21 self.type = type
22 message = "The tax application should be specified for this type."
23 super().__init__(message)
26class BenefitType(StrEnum):
27 custom = "custom"
28 discord = "discord"
29 github_repository = "github_repository"
30 downloadables = "downloadables"
31 license_keys = "license_keys"
32 meter_credit = "meter_credit"
34 def get_display_name(self) -> str:
35 return {
36 BenefitType.custom: "Custom",
37 BenefitType.discord: "Discord",
38 BenefitType.github_repository: "GitHub Repository",
39 BenefitType.downloadables: "Downloadables",
40 BenefitType.license_keys: "License Keys",
41 BenefitType.meter_credit: "Meter Credit",
42 }[self]
44 def is_tax_applicable(self) -> bool:
45 try:
46 _is_tax_applicable_map: dict[BenefitType, bool] = {
47 BenefitType.custom: True,
48 BenefitType.discord: True,
49 BenefitType.github_repository: True,
50 BenefitType.downloadables: True,
51 BenefitType.license_keys: True,
52 BenefitType.meter_credit: True,
53 }
54 return _is_tax_applicable_map[self]
55 except KeyError as e:
56 raise TaxApplicationMustBeSpecified(self) from e
59class Benefit(MetadataMixin, RecordModel):
60 __tablename__ = "benefits"
62 type: Mapped[BenefitType] = mapped_column(
63 StringEnum(BenefitType), nullable=False, index=True
64 )
65 description: Mapped[str] = mapped_column(Text, nullable=False)
66 is_tax_applicable: Mapped[bool] = mapped_column(
67 Boolean, nullable=False, default=False
68 )
69 selectable: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)
70 deletable: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)
71 organization_id: Mapped[UUID] = mapped_column(
72 Uuid,
73 ForeignKey("organizations.id", ondelete="cascade"),
74 nullable=False,
75 index=True,
76 )
78 @declared_attr
79 def properties(cls) -> Mapped["BenefitProperties"]:
80 return mapped_column("properties", JSONB, nullable=False, default=dict)
82 @declared_attr
83 def organization(cls) -> Mapped["Organization"]:
84 return relationship("Organization", lazy="raise")
86 @declared_attr
87 def grants(cls) -> Mapped["list[BenefitGrant]"]:
88 return relationship(
89 "BenefitGrant", lazy="raise", back_populates="benefit", viewonly=True
90 )