Skip to content

Api reference

components.premium.public.api

PremiumAggregationService

Retrieve premiums, no matter which engine and storage format were used.

get_uninvoiced_premiums staticmethod

get_uninvoiced_premiums(
    *, policy: Policy, up_to: date
) -> list[PremiumEntry]
get_uninvoiced_premiums(
    *, contract: Contract, up_to: date
) -> list[PremiumEntry]
get_uninvoiced_premiums(
    *, policy=None, contract=None, up_to
)

Retrieve premium entries with uninvoiced components, checking specifically if the company or primary part is invoiced.

Returns complete premium entries where at least one component has not been invoiced yet. The debtor is inferred from the contract type: - Policy: primary policy holder - Contract → company

Parameters:

Name Type Description Default
policy Policy | None

Policy to retrieve premiums for (mutually exclusive with contract).

None
contract Contract | None

Contract to retrieve premiums for (mutually exclusive with policy).

None
up_to date

Only returns entries where period_end <= up_to.

required

Returns:

Type Description
list[PremiumEntry]

List of complete PremiumEntry objects with uninvoiced components.

Source code in components/premium/public/api.py
@staticmethod
def get_uninvoiced_premiums(
    *,
    policy: Policy | None = None,
    contract: Contract | None = None,
    up_to: date,
) -> list[PremiumEntry]:
    """
    Retrieve premium entries with uninvoiced components, checking specifically if
    the company or primary part is invoiced.

    Returns complete premium entries where at least one component has not been
    invoiced yet. The debtor is inferred from the contract type:
      - Policy: primary policy holder
      - Contract → company

    Args:
        policy: Policy to retrieve premiums for (mutually exclusive with contract).
        contract: Contract to retrieve premiums for (mutually exclusive with policy).
        up_to: Only returns entries where period_end <= up_to.

    Returns:
        List of complete PremiumEntry objects with uninvoiced components.
    """
    repository = get_app_dependency().get_premium_repository(session=None)

    if policy:
        debtor = InsuranceBilledEntity.primary
        enrollment_ids = [enrollment.id for enrollment in policy.enrollments]
    elif contract:
        debtor = InsuranceBilledEntity.company
        enrollment_ids = [
            enrollment.id
            for policy in contract.policies
            for enrollment in policy.enrollments
        ]
    else:
        raise ValueError("Either policy or contract must be provided")

    return repository.get_all_uninvoiced_entries_up_to(
        enrollment_ids=enrollment_ids,
        debtor=debtor,
        up_to=up_to,
    )

mark_premiums_as_invoiced staticmethod

mark_premiums_as_invoiced(
    session, premium_entries, *, invoice_id, debtor
)

Mark premium entries as invoiced by setting invoice_id on matching components.

Parameters:

Name Type Description Default
session Session

Database session.

required
premium_entries list[PremiumEntry]

List of premium entries to mark as invoiced.

required
invoice_id UUID

The invoice ID to set on matching components.

required
debtor InsuranceBilledEntity

The billed entity whose components should be marked as invoiced.

required
Source code in components/premium/public/api.py
@staticmethod
@transactional
def mark_premiums_as_invoiced(
    session: Session,
    premium_entries: list[PremiumEntry],
    *,
    invoice_id: UUID,
    debtor: InsuranceBilledEntity,
) -> None:
    """
    Mark premium entries as invoiced by setting invoice_id on matching components.

    Args:
        session: Database session.
        premium_entries: List of premium entries to mark as invoiced.
        invoice_id: The invoice ID to set on matching components.
        debtor: The billed entity whose components should be marked as invoiced.
    """
    repository = get_app_dependency().get_premium_repository(session)

    updated_entries = [
        entry.with_components_marked_as_invoiced(invoice_id, debtor=debtor)
        for entry in premium_entries
    ]

    repository.update(updated_entries)

PremiumComputationService

Compute premiums.

Not responsible for storing the computed premiums

compute_insurance_premiums staticmethod

compute_insurance_premiums(
    session,
    *,
    policy,
    contract,
    start_month,
    end_month,
    engine_parameters=None
)

Compute how much money is owed to Alan and persist the results.

Parameters:

Name Type Description Default
session Session

SQLAlchemy session injected by @transactional decorator

required
policy Policy

The insurance policy containing enrollments

required
contract Contract

The contract defining pricing terms

required
start_month Month

First month to calculate premiums for

required
end_month Month

Last month to include in premium calculations

required
engine_parameters EngineParameters | None

Optional engine configuration. If not provided, uses defaults for current application

None

Returns:

Type Description
list[PremiumEntry]

All fresh entries returned by the computation.

Source code in components/premium/public/api.py
@staticmethod
@transactional()
def compute_insurance_premiums(
    session: Session,
    *,
    policy: Policy,
    contract: Contract,
    start_month: Month,
    end_month: Month,
    engine_parameters: EngineParameters | None = None,
) -> list[PremiumEntry]:
    """
    Compute how much money is owed to Alan and persist the results.

    Args:
        session: SQLAlchemy session injected by @transactional decorator
        policy: The insurance policy containing enrollments
        contract: The contract defining pricing terms
        start_month: First month to calculate premiums for
        end_month: Last month to include in premium calculations
        engine_parameters: Optional engine configuration. If not provided, uses
            defaults for current application

    Returns:
        All fresh entries returned by the computation.
    """
    repository = get_app_dependency().get_premium_repository(session)
    engine_parameters = engine_parameters or EngineParameters.default(
        app_name=get_current_app_name()
    )

    affiliation_builder = AffiliationTimelineBuilder(
        policy=policy,
        contract=contract,
        engine_parameters=engine_parameters,
    )

    affiliation_timeline = affiliation_builder.compute_affiliation_timeline(
        start_date=start_month.first_day,
        end_date=end_month.last_day,
    )

    cost_timeline = map_timeline(
        affiliation_timeline,
        lambda period: CostPeriod(
            validity_period=period.validity_period,
            start_reason=period.start_reason,
            cost=InsuranceCostProviderService.get_insurance_cost(
                policy_id=policy.id,
                on_date=period.validity_period.start_date,
                beneficiary_specs=period.beneficiary_specs,
                rounding_strategy=engine_parameters.rounding_strategy,
            ),
        ),
    )

    all_new_entries = compute_subscription_fees(
        cost_timeline=cost_timeline,
        periodicity=Periodicity.monthly,
        start=start_month,
        end=end_month,
        engine_parameters=engine_parameters,
    )

    enrollment_ids = [enrollment.id for enrollment in policy.enrollments]
    existing_premiums = repository.get_latest_entries(
        enrollment_ids=enrollment_ids,
        period_start=start_month,
        period_end=end_month,
    )

    result = reconcile_premium_entries(
        new_premiums=all_new_entries,
        existing_premiums=existing_premiums,
    )

    repository.update(result["existing_entries_to_update"])
    repository.insert(
        result["cancelling_entries_to_insert"] + result["new_entries_to_insert"]
    )

    return all_new_entries

components.premium.public.commands

components.premium.public.dependencies

COMPONENT_NAME module-attribute

COMPONENT_NAME = 'premium'

Canonical name of the premium component.

PremiumDependency

Bases: ABC

Represents all the dependency required by components/premium to function as expected.

get_premium_repository abstractmethod

get_premium_repository(session)

Instantiate a persistence layer for premium repository

Source code in components/premium/public/dependencies.py
@abstractmethod
def get_premium_repository(self, session: Session | None) -> PremiumEntryRepository:
    """
    Instantiate a persistence layer for premium repository
    """
    ...

get_app_dependency

get_app_dependency()

Function used to fetch the dependencies from the flask app.

Source code in components/premium/public/dependencies.py
def get_app_dependency() -> PremiumDependency:
    """Function used to fetch the dependencies from the flask app."""
    from flask import current_app

    return cast("CustomFlask", current_app).get_component_dependency(COMPONENT_NAME)  # type: ignore[no-any-return]

set_app_dependency

set_app_dependency(dependency)

Function used to actually inject the dependency class in the component.

Source code in components/premium/public/dependencies.py
def set_app_dependency(dependency: PremiumDependency) -> None:
    """Function used to actually inject the dependency class in the component."""
    from flask import current_app

    cast("CustomFlask", current_app).add_component_dependency(
        COMPONENT_NAME, dependency
    )

components.premium.public.entities

AgeStrategy

Bases: AlanBaseEnum

Strategy for handling age calculation in insurance premium calculations.

The effective birthday or age we use for pricing a beneficiary may differ from their actual birthday or age.

KEEP_EXACT_BIRTHDAY class-attribute instance-attribute

KEEP_EXACT_BIRTHDAY = 'exact_date'

Use the exact birthday for age calculation.

Example: If a member turns 23 on March 15, 2023, their age changes exactly on March 15, 2023.

MOVE_BIRTHDAY_TO_FIRST_DAY_OF_MONTH class-attribute instance-attribute

MOVE_BIRTHDAY_TO_FIRST_DAY_OF_MONTH = (
    "move_to_first_day_of_month"
)

Move birthday to the first day of the birth month for age calculation.

Example: If a member turns 23 on March 15, 2023, they will be priced as 23 years old starting March 1, 2023 for the entire month.

MOVE_BIRTHDAY_TO_JAN_OF_NEXT_YEAR class-attribute instance-attribute

MOVE_BIRTHDAY_TO_JAN_OF_NEXT_YEAR = 'jan_of_next_year'

Move birthday to January 1st of the year following the birth year.

Example: If a member turns 23 on March 15, 2023, they will be priced as 22 years old for the entirety of 2023, and 23 years old starting January 1, 2024.

Contract

Bases: Protocol

get_simple_subscription_version_timeline

get_simple_subscription_version_timeline()
Source code in components/premium/internal/domain/insurance_entities.py
def get_simple_subscription_version_timeline(
    self,
) -> Timeline[SimpleSubscriptionVersion]: ...

is_cancelled instance-attribute

is_cancelled

policies instance-attribute

policies

CostPeriod dataclass

CostPeriod(*, start_reason, cost)

Bases: GenericPeriodWithStartReason

Represents a timeline period with computed premium cost.

Final output of the premium calculation pipeline, containing the actual cost for the stable period. Each period guarantees the cost remains constant throughout its validity period.

Attributes:

Name Type Description
cost Cost

The computed insurance cost for this period, calculated based on the stable beneficiary composition and contract terms.

cost instance-attribute

cost

EngineParameters dataclass

EngineParameters(
    *,
    age_strategy,
    rounding_strategy,
    prorata_strategy,
    default_child_age,
    default_adult_age
)

Parameters dictating how the engine behaves and which rules it follows.

Example
  • which effective birth date to use
  • which prorata formula to use
  • which rounding strategy to use

age_strategy instance-attribute

age_strategy

default staticmethod

default(app_name)

Get default engine parameters for a given application. Application is used as a proxy for country.

Parameters:

Name Type Description Default
name

The application name to get default parameters for

required

Raises:

Type Description
ValueError

If the application is not yet supported

Source code in components/premium/internal/domain/engine_parameters.py
@staticmethod
def default(app_name: AppName) -> "EngineParameters":
    """
    Get default engine parameters for a given application.
    Application is used as a proxy for country.

    Args:
        name: The application name to get default parameters for

    Raises:
        ValueError: If the application is not yet supported
    """
    match app_name:
        case AppName.ALAN_BE:
            return EngineParameters(
                age_strategy=AgeStrategy.MOVE_BIRTHDAY_TO_FIRST_DAY_OF_MONTH,
                rounding_strategy=RoundingStrategy.BANKERS,
                prorata_strategy=ProrataStrategy.THIRTY_DAY_PRORATA_WITH_LARGEST_REMAINDER_DISTRIBUTION_ACROSS_FEE_COMPONENTS,
                default_child_age=17,
                default_adult_age=25,
            )
        case AppName.ALAN_FR:
            return EngineParameters(
                age_strategy=AgeStrategy.MOVE_BIRTHDAY_TO_JAN_OF_NEXT_YEAR,
                rounding_strategy=RoundingStrategy.BANKERS,
                prorata_strategy=ProrataStrategy.THIRTY_DAY_PRORATA_WITH_LARGEST_REMAINDER_DISTRIBUTION_ACROSS_FEE_COMPONENTS,
                default_child_age=17,
                default_adult_age=25,
            )
        case _:
            raise ValueError(f"Unsupported app_name: {app_name}")

default_adult_age instance-attribute

default_adult_age

Optional strategy for normalizing ages into specific buckets for pricing. When None, ages are used directly from beneficiary birthdates or defaults. When specified, the strategy determines how to group beneficiaries by age.

default_child_age instance-attribute

default_child_age

prorata_strategy instance-attribute

prorata_strategy

rounding_strategy instance-attribute

rounding_strategy

FeeComponent dataclass

FeeComponent(
    *,
    id=uuid4(),
    num_days,
    invoice_id,
    amount_before_prorata
)

Bases: CostComponent

A fee component represents one part of a fee that we intend to bill to a customer.

__eq__

__eq__(other)
Source code in components/premium/internal/domain/fee.py
def __eq__(self, other: object) -> bool:
    if not isinstance(other, FeeComponent):
        return False

    return (
        self.num_days == other.num_days
        and self.amount == other.amount
        and self.amount_before_prorata == other.amount_before_prorata
        and self.currency == other.currency
        and self.beneficiary_type == other.beneficiary_type
        and self.service_type == other.service_type
        and self.contribution_type == other.contribution_type
        and self.billed_entity == other.billed_entity
        and self.collection_method == other.collection_method
        and self.enrollment_id == other.enrollment_id
    )

__hash__

__hash__()
Source code in components/premium/internal/domain/fee.py
def __hash__(self) -> int:
    return hash(
        (
            self.num_days,
            self.amount,
            self.amount_before_prorata,
            self.currency.alphabetic_code,
            self.beneficiary_type,
            self.service_type,
            self.contribution_type,
            self.billed_entity,
            self.collection_method,
            str(self.enrollment_id) if self.enrollment_id else None,
        )
    )

amount_before_prorata instance-attribute

amount_before_prorata

Amount for full period coverage without prorata applied. Expressed in the minor unit of the currency. This is the full monthly amount that would be charged for complete period coverage.

as_tuple

as_tuple()

Return (billed_entity, contribution_type, amount)

Source code in components/premium/internal/domain/fee.py
def as_tuple(
    self,
) -> tuple["InsuranceBilledEntity", "InsuranceContribution", int]:
    """Return (billed_entity, contribution_type, amount)"""

    return (self.billed_entity, self.contribution_type, self.amount)

id class-attribute instance-attribute

id = field(default_factory=uuid4)

Unique identifier of the entry

inverted

inverted()

Returns a new FeeComponent offsetting the current one.

Source code in components/premium/internal/domain/fee.py
def inverted(self) -> "FeeComponent":
    """
    Returns a new FeeComponent offsetting the current one.
    """
    return FeeComponent(
        num_days=-self.num_days,
        amount=-self.amount,
        amount_before_prorata=-self.amount_before_prorata,
        currency=self.currency,
        beneficiary_type=self.beneficiary_type,
        service_type=self.service_type,
        contribution_type=self.contribution_type,
        billed_entity=self.billed_entity,
        collection_method=self.collection_method,
        enrollment_id=self.enrollment_id,
        invoice_id=None,
    )

invoice_id instance-attribute

invoice_id

If the component is included in an invoice, it is represented here.

num_days instance-attribute

num_days

Number of days this fee covers within the billing period.

LegacyBelgianNormalization dataclass

LegacyBelgianNormalization()

Groups beneficiaries into specific age buckets: - Children are treated as adults if they are 25 or above - Young adults (18-25) are priced as 25 or above if they are the primary policy holder or their partner - No birthdate available: default to age 25 - Anyone not falling in one of the buckets above: use their actual age

CHILD_AGE_LIMIT_YEARS class-attribute instance-attribute

CHILD_AGE_LIMIT_YEARS = 25

normalize_age

normalize_age(
    effective_birthdate, enrollment_type, reference_date
)
Source code in components/premium/internal/domain/age_normalization.py
def normalize_age(
    self,
    effective_birthdate: date | None,
    enrollment_type: EnrollmentType,
    reference_date: date,
) -> int:
    if not effective_birthdate:
        return self.CHILD_AGE_LIMIT_YEARS

    age = age_on(effective_birthdate, reference_date)

    if enrollment_type != EnrollmentType.child and age < self.CHILD_AGE_LIMIT_YEARS:
        return self.CHILD_AGE_LIMIT_YEARS

    if enrollment_type == EnrollmentType.child and age < self.CHILD_AGE_LIMIT_YEARS:
        return 17

    return age

Policy

Bases: Historizable

enrollments instance-attribute

enrollments

id instance-attribute

id

PremiumEntry dataclass

PremiumEntry(
    *,
    id=uuid4(),
    enrollment_id,
    components,
    period_start,
    period_end,
    version=1,
    cancelled_by_entry_id=None,
    cancelled_entry_id=None
)

A subscription fee specific to insurance subscriptions.

__eq__

__eq__(other)
Source code in components/premium/internal/domain/fee.py
def __eq__(self, other: object) -> bool:
    if not isinstance(other, PremiumEntry):
        return False

    return (
        self.enrollment_id == other.enrollment_id
        and self.period_start == other.period_start
        and self.period_end == other.period_end
        and set(self.components) == set(other.components)
    )

__hash__

__hash__()
Source code in components/premium/internal/domain/fee.py
def __hash__(self) -> int:
    return hash(
        (
            str(self.enrollment_id),
            self.period_start,
            self.period_end,
            frozenset(self.components),
        )
    )

beneficiary_type property

beneficiary_type

cancelled

cancelled()

Returns two new entries: one marking this entry as cancelled, and one offsetting it.

Returns:

Type Description
tuple[PremiumEntry, PremiumEntry | None]

tuple[PremiumEntry, PremiumEntry]: - First entry: Same as self but marked as cancelled - Second entry: New entry to offset self.

Source code in components/premium/internal/domain/fee.py
def cancelled(self) -> tuple["PremiumEntry", "PremiumEntry | None"]:
    """
    Returns two new entries: one marking this entry as cancelled, and one offsetting it.

    Returns:
        tuple[PremiumEntry, PremiumEntry]:
            - First entry: Same as self but marked as cancelled
            - Second entry: New entry to offset self.
    """

    # If B cancels A and we cancel B, it's equivalent to undoing the cancelation of
    # A
    # Therefore, canceling entries should never get canceled.
    if self.cancelled_entry_id:
        return self, None

    if self.cancelled_by_entry_id:
        return self, None

    cancelling_entry_id = uuid4()

    cancelled_entry = PremiumEntry(
        id=self.id,
        enrollment_id=self.enrollment_id,
        components=self.components,
        period_start=self.period_start,
        period_end=self.period_end,
        version=self.version,
        cancelled_entry_id=self.cancelled_entry_id,
        cancelled_by_entry_id=cancelling_entry_id,
    )

    offsetting_entry = PremiumEntry(
        id=cancelling_entry_id,
        enrollment_id=self.enrollment_id,
        components=[component.inverted() for component in self.components],
        period_start=self.period_start,
        period_end=self.period_end,
        version=self.version + 1,
        cancelled_by_entry_id=None,
        cancelled_entry_id=self.id,
    )

    return cancelled_entry, offsetting_entry

cancelled_by_entry_id class-attribute instance-attribute

cancelled_by_entry_id = field(default=None)

cancelled_entry_id class-attribute instance-attribute

cancelled_entry_id = field(default=None)

components instance-attribute

components

components_billed_to_debtor

components_billed_to_debtor(debtor)

Returns components that are actually billed to the specified debtor.

This method accounts for collection methods: - If debtor is company: includes company components AND primary components with payroll - If debtor is primary: only includes primary components with direct_billing

Source code in components/premium/internal/domain/fee.py
def components_billed_to_debtor(
    self, debtor: "InsuranceBilledEntity"
) -> list[FeeComponent]:
    """
    Returns components that are actually billed to the specified debtor.

    This method accounts for collection methods:
    - If debtor is company: includes company components AND primary components with payroll
    - If debtor is primary: only includes primary components with direct_billing
    """
    return [
        c for c in self.components if self._is_component_billed_to_debtor(c, debtor)
    ]

components_for_debtor

components_for_debtor(debtor)

The debtor is not necessarily the entity being billed

If a component's debtor is the primary but the collection method is payroll, the company gets billed.

If your purpose is to list components that should be invoiced to a given debtor, use components_billed_to_debtor().

Source code in components/premium/internal/domain/fee.py
def components_for_debtor(
    self, debtor: "InsuranceBilledEntity"
) -> list[FeeComponent]:
    """
    !!! warning "The debtor is not necessarily the entity being billed"

        If a component's debtor is the primary but the collection method is payroll,
        the company gets billed.

        If your purpose is to list components that should be invoiced to a given debtor,
        use `components_billed_to_debtor()`.
    """
    return [c for c in self.components if c.billed_entity == debtor]

currency property

currency

enrollment_id instance-attribute

enrollment_id

id class-attribute instance-attribute

id = field(default_factory=uuid4)

num_days property

num_days

period_end instance-attribute

period_end

period_start instance-attribute

period_start

pretax_amount_billed_to_debtor

pretax_amount_billed_to_debtor(debtor)

Returns the total pretax amount (membership fees + costs) billed to the specified debtor.

This includes components with contribution_type of: - InsuranceContribution.membership_fee: Base membership fees - InsuranceContribution.cost: Insurance coverage costs

This method accounts for collection methods: - If debtor is company: includes company components AND primary components with payroll - If debtor is primary: only includes primary components with direct_billing

Parameters:

Name Type Description Default
debtor InsuranceBilledEntity

The entity to which costs are billed

required

Returns:

Type Description
int

Total pretax amount in minor currency units

Source code in components/premium/internal/domain/fee.py
def pretax_amount_billed_to_debtor(self, debtor: "InsuranceBilledEntity") -> int:
    """
    Returns the total pretax amount (membership fees + costs) billed to the specified debtor.

    This includes components with contribution_type of:
    - InsuranceContribution.membership_fee: Base membership fees
    - InsuranceContribution.cost: Insurance coverage costs

    This method accounts for collection methods:
    - If debtor is company: includes company components AND primary components with payroll
    - If debtor is primary: only includes primary components with direct_billing

    Args:
        debtor: The entity to which costs are billed

    Returns:
        Total pretax amount in minor currency units
    """
    return sum(
        c.amount
        for c in self.components
        if self._is_component_billed_to_debtor(c, debtor)
        and c.contribution_type
        in [
            InsuranceContribution.membership_fee,
            InsuranceContribution.cost,
        ]
    )

prorata_ratio

prorata_ratio()

Returns the prorata ratio for this premium entry.

The ratio is calculated as: - 1.0 (or -1.0 for negative days) if the premium covers the full month - num_days / 30 otherwise (using 30-day convention, rounded to 2 decimals)

Uses arithmetic rounding (ROUND_HALF_UP) for consistency with invoicing display.

Returns:

Type Description
float

Prorata ratio as a float. Positive for regular premiums, negative for credits/reversals.

Source code in components/premium/internal/domain/fee.py
def prorata_ratio(self) -> float:
    """
    Returns the prorata ratio for this premium entry.

    The ratio is calculated as:
    - 1.0 (or -1.0 for negative days) if the premium covers the full month
    - num_days / 30 otherwise (using 30-day convention, rounded to 2 decimals)

    Uses arithmetic rounding (ROUND_HALF_UP) for consistency with invoicing display.

    Returns:
        Prorata ratio as a float. Positive for regular premiums, negative for credits/reversals.
    """
    from shared.helpers.math import arithmetic_round

    if abs(self.num_days) == Month(self.period_start).n_days:
        return 1.0 if self.num_days > 0 else -1.0
    else:
        return arithmetic_round(self.num_days / 30, ndigits=2)

service_type property

service_type

taxes_billed_to_debtor

taxes_billed_to_debtor(debtor)

Returns the total tax amount billed to the specified debtor.

This includes components with contribution_type of: - InsuranceContribution.taxes: Tax components

This method accounts for collection methods: - If debtor is company: includes company components AND primary components with payroll - If debtor is primary: only includes primary components with direct_billing

Parameters:

Name Type Description Default
debtor InsuranceBilledEntity

The entity to which taxes are billed

required

Returns:

Type Description
int

Total tax amount in minor currency units

Source code in components/premium/internal/domain/fee.py
def taxes_billed_to_debtor(self, debtor: "InsuranceBilledEntity") -> int:
    """
    Returns the total tax amount billed to the specified debtor.

    This includes components with contribution_type of:
    - InsuranceContribution.taxes: Tax components

    This method accounts for collection methods:
    - If debtor is company: includes company components AND primary components with payroll
    - If debtor is primary: only includes primary components with direct_billing

    Args:
        debtor: The entity to which taxes are billed

    Returns:
        Total tax amount in minor currency units
    """
    return sum(
        c.amount
        for c in self.components
        if self._is_component_billed_to_debtor(c, debtor)
        and c.contribution_type == InsuranceContribution.taxes
    )

total_amount

total_amount()
Source code in components/premium/internal/domain/fee.py
def total_amount(self) -> int:
    return sum(c.amount for c in self.components)

total_amount_billed_to_debtor

total_amount_billed_to_debtor(debtor)

Returns the total amount for components actually billed to the specified debtor.

This method accounts for collection methods: - If debtor is company: includes company components AND primary components with payroll - If debtor is primary: only includes primary components with direct_billing

Source code in components/premium/internal/domain/fee.py
def total_amount_billed_to_debtor(self, debtor: "InsuranceBilledEntity") -> int:
    """
    Returns the total amount for components actually billed to the specified debtor.

    This method accounts for collection methods:
    - If debtor is company: includes company components AND primary components with payroll
    - If debtor is primary: only includes primary components with direct_billing
    """
    return sum(
        c.amount
        for c in self.components
        if self._is_component_billed_to_debtor(c, debtor)
    )

total_amount_for_debtor

total_amount_for_debtor(debtor)

The debtor is not necessarily the entity being billed

If a component's debtor is the primary but the collection method is payroll, the company gets billed.

If your purpose is to compute the total amount that should be invoiced to a given debtor, use total_amount_billed_to_debtor().

Source code in components/premium/internal/domain/fee.py
def total_amount_for_debtor(self, debtor: "InsuranceBilledEntity") -> int:
    """
    !!! warning "The debtor is not necessarily the entity being billed"

        If a component's debtor is the primary but the collection method is payroll,
        the company gets billed.

        If your purpose is to compute the total amount that should be invoiced to a given debtor,
        use `total_amount_billed_to_debtor()`.
    """
    return sum(c.amount for c in self.components if c.billed_entity == debtor)

total_billed_to_debtor_without_prorata

total_billed_to_debtor_without_prorata(debtor)

Returns the total amount before prorata for components actually billed to the specified debtor.

This method accounts for collection methods: - If debtor is company: includes company components AND primary components with payroll - If debtor is primary: only includes primary components with direct_billing

Source code in components/premium/internal/domain/fee.py
def total_billed_to_debtor_without_prorata(
    self, debtor: "InsuranceBilledEntity"
) -> int:
    """
    Returns the total amount before prorata for components actually billed to the specified debtor.

    This method accounts for collection methods:
    - If debtor is company: includes company components AND primary components with payroll
    - If debtor is primary: only includes primary components with direct_billing
    """
    return sum(
        c.amount_before_prorata
        for c in self.components
        if self._is_component_billed_to_debtor(c, debtor)
    )

total_for_debtor_without_prorata

total_for_debtor_without_prorata(debtor)

The debtor is not necessarily the entity being billed

If a component's debtor is the primary but the collection method is payroll, the company gets billed.

If your purpose is to compute the total amount without prorata that should be invoiced to a given debtor, use total_billed_to_debtor_without_prorata().

Source code in components/premium/internal/domain/fee.py
def total_for_debtor_without_prorata(self, debtor: "InsuranceBilledEntity") -> int:
    """
    !!! warning "The debtor is not necessarily the entity being billed"

        If a component's debtor is the primary but the collection method is payroll,
        the company gets billed.

        If your purpose is to compute the total amount without prorata that should be invoiced to a given debtor,
        use `total_billed_to_debtor_without_prorata()`.
    """
    return sum(
        c.amount_before_prorata
        for c in self.components
        if c.billed_entity == debtor
    )

version class-attribute instance-attribute

version = field(default=1)

with_components_marked_as_invoiced

with_components_marked_as_invoiced(invoice_id, *, debtor)

Returns a new PremiumEntry with components for the specified debtor marked as invoiced.

Parameters:

Name Type Description Default
invoice_id UUID

The invoice ID to set on matching components.

required
debtor InsuranceBilledEntity

The billed entity whose components should be marked as invoiced.

required

Returns:

Type Description
PremiumEntry

New PremiumEntry with updated components.

Source code in components/premium/internal/domain/fee.py
def with_components_marked_as_invoiced(
    self, invoice_id: UUID, *, debtor: "InsuranceBilledEntity"
) -> "PremiumEntry":
    """
    Returns a new PremiumEntry with components for the specified debtor marked as invoiced.

    Args:
        invoice_id: The invoice ID to set on matching components.
        debtor: The billed entity whose components should be marked as invoiced.

    Returns:
        New PremiumEntry with updated components.
    """
    updated_components = [
        FeeComponent(
            id=component.id,
            num_days=component.num_days,
            amount=component.amount,
            amount_before_prorata=component.amount_before_prorata,
            currency=component.currency,
            beneficiary_type=component.beneficiary_type,
            service_type=component.service_type,
            contribution_type=component.contribution_type,
            billed_entity=component.billed_entity,
            collection_method=component.collection_method,
            enrollment_id=component.enrollment_id,
            invoice_id=invoice_id
            if self._is_component_billed_to_debtor(component, debtor)
            else component.invoice_id,
        )
        for component in self.components
    ]

    return PremiumEntry(
        id=self.id,
        enrollment_id=self.enrollment_id,
        components=updated_components,
        period_start=self.period_start,
        period_end=self.period_end,
        version=self.version,
        cancelled_by_entry_id=self.cancelled_by_entry_id,
        cancelled_entry_id=self.cancelled_entry_id,
    )

with_version

with_version(version)

Returns a new PremiumEntry with a different version.

Source code in components/premium/internal/domain/fee.py
def with_version(self, version: int) -> "PremiumEntry":
    """Returns a new PremiumEntry with a different version."""
    return PremiumEntry(
        id=self.id,
        enrollment_id=self.enrollment_id,
        components=self.components,
        period_start=self.period_start,
        period_end=self.period_end,
        version=version,
        cancelled_by_entry_id=self.cancelled_by_entry_id,
        cancelled_entry_id=self.cancelled_entry_id,
    )

PremiumEntryRepository

Bases: Protocol

Repository protocol for persisting and retrieving premium entries.

get_all_entries

get_all_entries(enrollment_ids, period_start, period_end)

Retrieve all premium entries for specified enrollments with an optional period.

Returns all stored premium entries including both cancelled and active entries.

Parameters:

Name Type Description Default
enrollment_ids list[int | UUID]

IDs (int or UUID) of enrollments to retrieve entries for. Some implementations may only support UUID IDs and will raise an error if int IDs are provided.

required
period_start Month | date | None

Optional lower bound for the entry period. If provided, only returns entries where entry.period_start == period_start. If Month is passed, use the first day of the month. If None, there is no filter on the start date of the period.

required
period_end Month | date | None

Optional upper bound for the entry period. If provided, only returns entries where entry.period_end == period_end. If Month is passed, use the last day of the month. If None, there is no filter on the end date of the period.

required

Returns:

Type Description
list[PremiumEntry]

List of PremiumEntry objects matching the filtering criteria.

list[PremiumEntry]

If both date parameters are None, returns all entries for the enrollments.

Source code in components/premium/internal/domain/repository.py
def get_all_entries(
    self,
    enrollment_ids: list[int | UUID],
    period_start: Month | date | None,
    period_end: Month | date | None,
) -> list[PremiumEntry]:
    """
    Retrieve all premium entries for specified enrollments with an optional period.

    Returns all stored premium entries including both cancelled and active entries.

    Args:
        enrollment_ids: IDs (int or UUID) of enrollments to retrieve entries for.
            Some implementations may only support UUID IDs and will raise an error
            if int IDs are provided.
        period_start: Optional lower bound for the entry period.
            If provided, only returns entries where entry.period_start == period_start.
            If Month is passed, use the first day of the month.
            If None, there is no filter on the start date of the period.
        period_end: Optional upper bound for the entry period.
            If provided, only returns entries where entry.period_end == period_end.
            If Month is passed, use the last day of the month.
            If None, there is no filter on the end date of the period.

    Returns:
        List of PremiumEntry objects matching the filtering criteria.
        If both date parameters are None, returns all entries for the enrollments.
    """
    ...

get_all_uninvoiced_entries_up_to

get_all_uninvoiced_entries_up_to(
    enrollment_ids, debtor, up_to
)

Retrieve premium entries that have uninvoiced components for a specific debtor.

An entry is included if it has at least one component where the debtor has not been invoiced (invoice_id IS NULL). The returned entries are complete - they contain all components regardless of their invoice status or debtor.

Parameters:

Name Type Description Default
enrollment_ids list[int | UUID]

IDs (int or UUID) of enrollments to retrieve entries for. Some implementations may only support UUID IDs and will raise an error if int IDs are provided.

required
debtor InsuranceBilledEntity

The billed entity (primary, company, etc.) to check for uninvoiced components.

required
up_to date

Only returns entries where period_end <= up_to.

required

Returns:

Type Description
list[PremiumEntry]

List of complete PremiumEntry objects.

Source code in components/premium/internal/domain/repository.py
def get_all_uninvoiced_entries_up_to(
    self,
    enrollment_ids: list[int | UUID],
    debtor: "InsuranceBilledEntity",
    up_to: date,
) -> list[PremiumEntry]:
    """
    Retrieve premium entries that have uninvoiced components for a specific debtor.

    An entry is included if it has at least one component where the debtor has not
    been invoiced (invoice_id IS NULL). The returned entries are complete - they
    contain all components regardless of their invoice status or debtor.

    Args:
        enrollment_ids: IDs (int or UUID) of enrollments to retrieve entries for.
            Some implementations may only support UUID IDs and will raise an error
            if int IDs are provided.
        debtor: The billed entity (primary, company, etc.) to check for uninvoiced components.
        up_to: Only returns entries where period_end <= up_to.

    Returns:
        List of complete PremiumEntry objects.
    """
    ...

get_latest_entries

get_latest_entries(
    enrollment_ids, period_start, period_end
)

Retrieve the latest premium entries for specified enrollments and period.

Note: For each period, an enrollee may have multiple entries.

Parameters:

Name Type Description Default
enrollment_ids list[int | UUID]

IDs (int or UUID) of enrollments to retrieve entries for. Some implementations may only support UUID IDs and will raise an error if int IDs are provided.

required
period_start Month | date

Exact start date of the period. If Month is passed, use the first day of the month.

required
period_end Month | date

Exact end date of the period. If Month is passed, use the last day of the month.

required

Returns:

Type Description
list[PremiumEntry]

List of PremiumEntry objects representing the latest entries

list[PremiumEntry]

for each enrollment with the specified period dates.

Source code in components/premium/internal/domain/repository.py
def get_latest_entries(
    self,
    enrollment_ids: list[int | UUID],
    period_start: Month | date,
    period_end: Month | date,
) -> list[PremiumEntry]:
    """
    Retrieve the latest premium entries for specified enrollments and period.

    Note: For each period, an enrollee may have multiple entries.

    Args:
        enrollment_ids: IDs (int or UUID) of enrollments to retrieve entries for.
            Some implementations may only support UUID IDs and will raise an error
            if int IDs are provided.
        period_start: Exact start date of the period.
             If Month is passed, use the first day of the month.
        period_end: Exact end date of the period.
             If Month is passed, use the last day of the month.

    Returns:
        List of PremiumEntry objects representing the latest entries
        for each enrollment with the specified period dates.
    """
    ...

insert

insert(entries)

Insert new premium entries into the database.

Parameters:

Name Type Description Default
entries list[PremiumEntry]

List of PremiumEntry objects to insert. Each entry's components will be added to the database.

required
Source code in components/premium/internal/domain/repository.py
def insert(self, entries: list[PremiumEntry]) -> None:
    """
    Insert new premium entries into the database.

    Args:
        entries: List of PremiumEntry objects to insert.
                 Each entry's components will be added to the database.
    """
    ...

session instance-attribute

session

update

update(entries)

Update existing premium entries in the database.

Parameters:

Name Type Description Default
entries list[PremiumEntry]

List of PremiumEntry objects to update. Each entry's components will be merged into the database.

required
Source code in components/premium/internal/domain/repository.py
def update(self, entries: list[PremiumEntry]) -> None:
    """
    Update existing premium entries in the database.

    Args:
        entries: List of PremiumEntry objects to update.
                 Each entry's components will be merged into the database.
    """
    ...

ProrataStrategy

Bases: AlanBaseEnum

Strategy for applying prorata to cost components.

When multiple cost components need prorating (e.g., base cost, membership fee, taxes), different strategies can be used that may produce slightly different results due to rounding.

THIRTY_DAY_PRORATA class-attribute instance-attribute

THIRTY_DAY_PRORATA = '30_day_prorata'

If the service covers the whole month, bill the full amount for 1 month. A month is a full month if the number of days covered equals the number of days in the month. If the service does not cover the whole month, prorate on the base of 30 days.

Formula: monthly_price * days_covered / 30

This prorata is applied to all the components of the subscription fee and may carry rounding errors, meaning the total of components may exceed the initial total.

THIRTY_DAY_PRORATA_WITH_LARGEST_REMAINDER_DISTRIBUTION_ACROSS_FEE_COMPONENTS class-attribute instance-attribute

THIRTY_DAY_PRORATA_WITH_LARGEST_REMAINDER_DISTRIBUTION_ACROSS_FEE_COMPONENTS = "30_day_prorata_with_largest_remainder_distribution_across_fee_components"

Similar to THIRTY_DAY_PRORATA except rounding errors are avoided by using the largest remainder algorithm to distribute the last few cents across the component.

There is a risk of losing accuracy, for example attributing more to the membership fee than we should. But we have the guarantee the total of the components will never exceed the initial total and the inaccuracies should never exceed 1 cent.

SubscriptionPeriod dataclass

SubscriptionPeriod(*, fees)

Bases: GenericPeriod

fees instance-attribute

fees

components.premium.public.models

premium_component

PremiumComponentModel

Bases: BaseModel

A model describing how to store global premium entries...

Each country is responsible for reimplementing their own model, inheriting from this one.

__abstract__ class-attribute instance-attribute
__abstract__ = True
__table_args__ class-attribute instance-attribute
__table_args__ = (
    UniqueConstraint(
        "enrollment_id",
        "premium_entry_id",
        "coverage_type",
        "beneficiary_type",
        "debtor_type",
        "period_start",
        "period_end",
        "contribution_type",
        "version",
        name="one_component_across_all_dimensions_per_version",
    ),
    Index(
        "ix_enrollment_id_version",
        "enrollment_id",
        "version",
    ),
    Index(
        "ix_period_start_end", "period_start", "period_end"
    ),
)
amount class-attribute instance-attribute
amount = mapped_column(Integer, nullable=False)

Amount for num_days of coverage from period_start to period_end. Expressed in the minor unit of the currency.

amount_before_prorata class-attribute instance-attribute
amount_before_prorata = mapped_column(
    Integer, nullable=False
)

Amount for full coverage from period_start to period_end. Expressed in the minor unit of the currency. This is the amount that would be billed if we decided not to apply prorata.

beneficiary_type class-attribute instance-attribute
beneficiary_type = mapped_column(
    Enum(InsuranceBeneficiary), nullable=False
)
cancelled_by_entry_id class-attribute instance-attribute
cancelled_by_entry_id = mapped_column(
    UUID(as_uuid=True), nullable=True, index=True
)
cancelled_entry_id class-attribute instance-attribute
cancelled_entry_id = mapped_column(
    UUID(as_uuid=True), nullable=True, index=True
)
collection_method class-attribute instance-attribute
collection_method = mapped_column(
    Enum(InsuranceCollectionMethod, nullable=True)
)
contribution_type class-attribute instance-attribute
contribution_type = mapped_column(
    Enum(InsuranceContribution), nullable=False
)
coverage_type class-attribute instance-attribute
coverage_type = mapped_column(
    Enum(InsuranceService), nullable=False
)
currency class-attribute instance-attribute
currency = mapped_column(String, nullable=False)

ISO4217 compliant alpha code of the currency.

debtor_type class-attribute instance-attribute
debtor_type = mapped_column(
    Enum(InsuranceBilledEntity), nullable=False
)
enrollment_id class-attribute instance-attribute
enrollment_id = mapped_column(
    UUID(as_uuid=True), nullable=False
)
invoice_id class-attribute instance-attribute
invoice_id = mapped_column(nullable=True, index=True)
num_days class-attribute instance-attribute
num_days = mapped_column(Integer, nullable=False)

Number of days this fee covers within the billing period.

period_end class-attribute instance-attribute
period_end = mapped_column(Date, nullable=False)
period_start class-attribute instance-attribute
period_start = mapped_column(Date, nullable=False)
premium_entry_id class-attribute instance-attribute
premium_entry_id = mapped_column(
    UUID(as_uuid=True), nullable=False
)
version class-attribute instance-attribute
version = mapped_column(Integer, nullable=False)