Skip to content

Api reference

components.price_provider.public.api

InsuranceCostProviderService module-attribute

InsuranceCostProviderService = PricingService

PricingService

Service for computing price breakdowns.

get_breakdown staticmethod

get_breakdown(
    *,
    on_date: date,
    pricing_source: PricingSource,
    contract_identifier: None = None,
    rounding_strategy: RoundingStrategy = RoundingStrategy.ARITHMETIC
) -> PriceBreakdown
get_breakdown(
    *,
    on_date: date,
    pricing_source: None = None,
    contract_identifier: ContractIdentifier,
    rounding_strategy: RoundingStrategy = RoundingStrategy.ARITHMETIC
) -> PriceBreakdown
get_breakdown(
    *,
    on_date: date,
    beneficiary_specs: Sequence[PricingEnrollmentSpec],
    pricing_source: PricingSource,
    rounding_strategy: RoundingStrategy = RoundingStrategy.ARITHMETIC
) -> PriceBreakdown
get_breakdown(
    *,
    on_date,
    pricing_source=None,
    contract_identifier=None,
    beneficiary_specs=None,
    rounding_strategy=RoundingStrategy.ARITHMETIC
)

Compute a price breakdown.

This price breakdown is evaluated based on
  • all coverage modules active on the given date
  • all beneficiaries active on the given date
  • all discounts active on the given date

When evaluating costs for a policy, if several coverage modules are active, the breakdown contains the aggregated cost components of all the coverage modules.

Parameters:

Name Type Description Default
on_date date

The reference date for coverage and affiliation data.

required
pricing_source PricingSource | None

Abstract identifier for pricing resolution (policy_id, enrollment_id, etc.). Must be provided if contract_identifier is None.

None
contract_identifier ContractIdentifier | None

The contract identifier to compute costs for. Must be provided if pricing_source is None. Currently not implemented.

None
beneficiary_specs Sequence[PricingEnrollmentSpec] | None

Optional list of enrollment specs to use instead of the source's current beneficiaries. When provided with pricing_source, uses these beneficiaries with the source's pricing function.

None
rounding_strategy RoundingStrategy

Strategy for rounding monetary amounts when splitting costs between entities. Defaults to arithmetic rounding.

ARITHMETIC

Returns:

Type Description
PriceBreakdown

A PriceBreakdown containing detailed cost breakdown for 30 days of coverage.

Raises:

Type Description
ValueError

If neither pricing_source nor contract_identifier is provided.

ValueError

If no price breakdown is found for the source on this date.

NotImplementedError

If contract_identifier is used (not yet supported).

Examples:

# Use the beneficiary information corresponding to the state of the source
# at the date of on_date.
PricingService.get_breakdown(
    pricing_source=policy_id, on_date=on_date
)

# Use an arbitrary list of beneficiaries.
PricingService.get_breakdown(
    beneficiary_specs=[
        PricingEnrollmentSpec(...),
        PricingEnrollmentSpec(...),
    ],
    pricing_source=policy_id,
    on_date=on_date,
)
Source code in components/price_provider/public/api.py
@staticmethod
def get_breakdown(
    *,
    on_date: date,
    pricing_source: PricingSource | None = None,
    contract_identifier: "ContractIdentifier | None" = None,
    beneficiary_specs: Sequence[PricingEnrollmentSpec] | None = None,
    rounding_strategy: RoundingStrategy = RoundingStrategy.ARITHMETIC,
) -> "PriceBreakdown":
    """Compute a price breakdown.

    This price breakdown is evaluated based on:
        - all coverage modules active on the given date
        - all beneficiaries active on the given date
        - all discounts active on the given date

    When evaluating costs for a policy, if several coverage modules are active,
    the breakdown contains the aggregated cost components of all the coverage
    modules.

    Args:
        on_date: The reference date for coverage and affiliation data.
        pricing_source: Abstract identifier for pricing resolution (policy_id,
            enrollment_id, etc.). Must be provided if contract_identifier is None.
        contract_identifier: The contract identifier to compute costs for.
            Must be provided if pricing_source is None. Currently not implemented.
        beneficiary_specs: Optional list of enrollment specs to use instead of
            the source's current beneficiaries. When provided with pricing_source,
            uses these beneficiaries with the source's pricing function.
        rounding_strategy: Strategy for rounding monetary amounts when splitting
            costs between entities. Defaults to arithmetic rounding.

    Returns:
        A PriceBreakdown containing detailed cost breakdown for 30 days of coverage.

    Raises:
        ValueError: If neither pricing_source nor contract_identifier is provided.
        ValueError: If no price breakdown is found for the source on this date.
        NotImplementedError: If contract_identifier is used (not yet supported).

    Examples:
        ```python
        # Use the beneficiary information corresponding to the state of the source
        # at the date of on_date.
        PricingService.get_breakdown(
            pricing_source=policy_id, on_date=on_date
        )

        # Use an arbitrary list of beneficiaries.
        PricingService.get_breakdown(
            beneficiary_specs=[
                PricingEnrollmentSpec(...),
                PricingEnrollmentSpec(...),
            ],
            pricing_source=policy_id,
            on_date=on_date,
        )
        ```
    """
    price_breakdown = None

    if beneficiary_specs and pricing_source:
        from components.price_provider.public.dependencies import get_app_dependency

        deps = get_app_dependency()
        price_breakdown = deps.get_price_breakdown(
            pricing_context=PricingContext(
                pricing_source=pricing_source,
                on_date=on_date,
                pricing_enrollment_specs=beneficiary_specs,
                rounding_strategy=rounding_strategy,
            ),
        )

    elif pricing_source:
        from components.price_provider.public.dependencies import get_app_dependency

        deps = get_app_dependency()
        pricing_context = deps.resolve_pricing_context(
            pricing_source=pricing_source,
            on_date=on_date,
        )
        price_breakdown = deps.get_price_breakdown(
            pricing_context=pricing_context,
        )

    elif contract_identifier:
        raise NotImplementedError(
            "get_breakdown by contract_identifier not implemented yet"
        )
    else:
        raise ValueError(
            "Must provide exactly one of pricing_source or contract_identifier"
        )

    if price_breakdown is None:
        raise ValueError("No price breakdown found")

    return price_breakdown

get_insurance_cost module-attribute

get_insurance_cost = get_breakdown

components.price_provider.public.dependencies

COMPONENT_NAME module-attribute

COMPONENT_NAME = 'price_provider'

Canonical name of the price provider component.

PriceProviderDependency

Bases: ABC

Dependencies injected into the price provider component.

Every component depending on the price provider component is responsible for injecting its own implementation of these dependencies.

get_price_breakdown abstractmethod

get_price_breakdown(*, pricing_context)

Compute price breakdown.

Implementation handles module -> PricingFunction resolution internally using pricing_context.pricing_source.

Parameters:

Name Type Description Default
pricing_context PricingContext

Inputs required to compute prices, including pricing_source, date, beneficiaries, and rounding strategy.

required

Returns:

Name Type Description
PriceBreakdown PriceBreakdown

A price breakdown with cost components.

Source code in components/price_provider/public/dependencies.py
@abstractmethod
def get_price_breakdown(
    self,
    *,
    pricing_context: PricingContext,
) -> PriceBreakdown:
    """Compute price breakdown.

    Implementation handles module -> PricingFunction resolution internally
    using pricing_context.pricing_source.

    Args:
        pricing_context: Inputs required to compute prices, including
            pricing_source, date, beneficiaries, and rounding strategy.

    Returns:
        PriceBreakdown: A price breakdown with cost components.
    """

resolve_pricing_context abstractmethod

resolve_pricing_context(*, pricing_source, on_date)

Resolve a pricing context from a pricing source.

Parameters:

Name Type Description Default
pricing_source PricingSource

Abstract identifier (policy_id, enrollment_id, etc.)

required
on_date date

The date for which to resolve the context.

required

Returns:

Type Description
PricingContext

A complete PricingContext with source, date, enrollment specs,

PricingContext

and implementation-specific rounding strategy.

Source code in components/price_provider/public/dependencies.py
@abstractmethod
def resolve_pricing_context(
    self,
    *,
    pricing_source: PricingSource,
    on_date: date,
) -> PricingContext:
    """Resolve a pricing context from a pricing source.

    Args:
        pricing_source: Abstract identifier (policy_id, enrollment_id, etc.)
        on_date: The date for which to resolve the context.

    Returns:
        A complete PricingContext with source, date, enrollment specs,
        and implementation-specific rounding strategy.
    """

get_app_dependency

get_app_dependency()

Function used to fetch the dependencies from the flask app.

Source code in components/price_provider/public/dependencies.py
def get_app_dependency() -> PriceProviderDependency:
    """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/price_provider/public/dependencies.py
def set_app_dependency(dependency: PriceProviderDependency) -> 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.price_provider.public.entities

Pricing API domain entities.

These types define the pricing-domain shapes used at component boundaries. Consumers should prefer local domain equivalents and map at adapters. For shared technical primitives (currency, rounding strategy), import from components.price_provider.public.primitives.

Note: Entities are defined in internal and re-exported here for external consumers.

BilledEntity

Bases: AlanBaseEnum

Represents an entity paying for a price component.

company class-attribute instance-attribute

company = 'company'

partner class-attribute instance-attribute

partner = 'partner'

primary class-attribute instance-attribute

primary = 'primary'

CollectionMethod

Bases: AlanBaseEnum

Methods for collecting a member's contribution.

direct_billing class-attribute instance-attribute

direct_billing = 'direct_billing'

The amount is paid directly by the employee or their partner, using the payment method of their choice.

payroll class-attribute instance-attribute

payroll = 'payroll'

The amount is paid by the company but automatically deducted from the employee's payslip.

ContributionType

Bases: AlanBaseEnum

Represents how an amount is used, what the money is going towards.

cost class-attribute instance-attribute

cost = 'cost'

discount class-attribute instance-attribute

discount = 'discount'

membership_fee class-attribute instance-attribute

membership_fee = 'membership_fee'

social_fund class-attribute instance-attribute

social_fund = 'social_fund'

taxes class-attribute instance-attribute

taxes = 'taxes'

MemberType

Bases: AlanBaseEnum

Represents the different member types.

children class-attribute instance-attribute

children = 'children'

from_enrollment_type classmethod

from_enrollment_type(enrollment_type)

Convert EnrollmentType to member type.

Source code in components/price_provider/internal/entities.py
@classmethod
def from_enrollment_type(cls, enrollment_type: EnrollmentType) -> "MemberType":
    """Convert EnrollmentType to member type."""
    if enrollment_type == EnrollmentType.primary:
        return cls.primary
    elif enrollment_type == EnrollmentType.partner:
        return cls.partner
    elif enrollment_type == EnrollmentType.child:
        return cls.children
    else:
        raise ValueError(f"Unknown enrollment type: {enrollment_type}")

partner class-attribute instance-attribute

partner = 'partner'

primary class-attribute instance-attribute

primary = 'primary'

to_enrollment_type

to_enrollment_type()

Convert member type to EnrollmentType.

Source code in components/price_provider/internal/entities.py
def to_enrollment_type(self) -> EnrollmentType:
    """Convert member type to EnrollmentType."""
    if self == MemberType.primary:
        return EnrollmentType.primary
    elif self == MemberType.partner:
        return EnrollmentType.partner
    elif self == MemberType.children:
        return EnrollmentType.child
    else:
        raise ValueError(f"Unknown member type: {self}")

Module

Bases: AlanBaseEnum

Represents a coverage module.

base class-attribute instance-attribute

base = 'base'

extended class-attribute instance-attribute

extended = 'extended'

Base + option rolled into the same coverage module.

hospitalization class-attribute instance-attribute

hospitalization = 'hospitalization'

Neutral label for single-coverage plans (no base/extended distinction).

option class-attribute instance-attribute

option = 'option'

PriceBreakdown dataclass

PriceBreakdown(*, cost_components)

Represents the full price breakdown.

No safeties on cost components

There are no checks verifying there are no duplicates in components. The caller is responsible for ensuring the components represent a valid cost breakdown.

Source code in components/price_provider/internal/entities.py
def __init__(self, *, cost_components: list[PriceComponent]):
    if len(cost_components) == 0:
        raise ValueError("No components to build a cost out of.")

    self.cost_components = cost_components
    self.currency = cost_components[0].currency

    # Compute some intermediary values frequently used by consumers, in O(n) time
    untaxed_amount_billed_to_primary = 0
    untaxed_amount_billed_to_company = 0

    taxes_billed_to_company = 0
    taxes_billed_to_primary = 0

    for component in cost_components:
        if component.currency != self.currency:
            raise ValueError("Cannot mix different currencies.")

        match component.billed_entity:
            case BilledEntity.company:
                if component.contribution_type == ContributionType.taxes:
                    taxes_billed_to_company += component.amount
                else:
                    untaxed_amount_billed_to_company += component.amount
            case BilledEntity.primary:
                if component.contribution_type == ContributionType.taxes:
                    taxes_billed_to_primary += component.amount
                else:
                    untaxed_amount_billed_to_primary += component.amount
            case billed_entity:
                raise NotImplementedError(
                    f"Unimplement billed entity: {billed_entity}"
                )

    taxed_amount_billed_to_primary = (
        untaxed_amount_billed_to_primary + taxes_billed_to_primary
    )
    taxed_amount_billed_to_company = (
        untaxed_amount_billed_to_company + taxes_billed_to_company
    )

    self.taxed_amount_billed_to_primary = taxed_amount_billed_to_primary
    self.untaxed_amount_billed_to_primary = untaxed_amount_billed_to_primary
    self.untaxed_amount_billed_to_company = untaxed_amount_billed_to_company
    self.taxed_amount_billed_to_company = taxed_amount_billed_to_company

    # Once initialised, the dataclass is frozen.
    # Enforced via custom __setattr__ implementation.
    self._initialized = True

__setattr__

__setattr__(name, value)

Setting attributes once the dataclass is initialized is forbidden. Reproducing the behavior of frozen=True.

Source code in components/price_provider/internal/entities.py
def __setattr__(self, name: str, value: object) -> None:
    """
    Setting attributes once the dataclass is initialized is forbidden.
    Reproducing the behavior of frozen=True.
    """
    if hasattr(self, "_initialized") and self._initialized:
        raise AttributeError(
            f"Cannot modify {name} after initialization. PriceBreakdown is immutable."
        )
    super().__setattr__(name, value)

cost_components instance-attribute

cost_components = cost_components

currency instance-attribute

currency = currency

from_components staticmethod

from_components(cost_components)

Construct a PriceBreakdown from several price components.

Raises:

Type Description
NotImplementedError

if the billed entity is not implemented yet

ValueError

if the currency used by all the components is not the same

Source code in components/price_provider/internal/entities.py
@staticmethod
def from_components(
    cost_components: list[PriceComponent],
) -> "PriceBreakdown":
    """
    Construct a PriceBreakdown from several price components.

    Raises:
        NotImplementedError: if the billed entity is not implemented yet
        ValueError: if the currency used by all the components is not the same
    """
    return PriceBreakdown(cost_components=cost_components)

merge staticmethod

merge(price_breakdowns)

Merge several price breakdowns into a single one.

When we evaluate the costs of coverage modules individually, merging them is useful to get the total aggregated costs.

Source code in components/price_provider/internal/entities.py
@staticmethod
def merge(
    price_breakdowns: list["PriceBreakdown"],
) -> "PriceBreakdown":
    """
    Merge several price breakdowns into a single one.

    When we evaluate the costs of coverage modules individually, merging them is
    useful to get the total aggregated costs.
    """
    if len(price_breakdowns) == 1:
        return price_breakdowns[0]

    all_components: list[PriceComponent] = []
    for breakdown in price_breakdowns:
        all_components.extend(breakdown.cost_components)
    return PriceBreakdown.from_components(all_components)

taxed_amount_billed_to_company instance-attribute

taxed_amount_billed_to_company = (
    taxed_amount_billed_to_company
)

taxed_amount_billed_to_primary instance-attribute

taxed_amount_billed_to_primary = (
    taxed_amount_billed_to_primary
)

untaxed_amount_billed_to_company instance-attribute

untaxed_amount_billed_to_company = (
    untaxed_amount_billed_to_company
)

untaxed_amount_billed_to_primary instance-attribute

untaxed_amount_billed_to_primary = (
    untaxed_amount_billed_to_primary
)

PriceComponent dataclass

PriceComponent(
    *,
    service_type,
    contribution_type,
    beneficiary_type,
    billed_entity,
    collection_method=None,
    enrollment_id=None,
    currency,
    amount,
    periodicity="monthly"
)

Represents part of a price.

__post_init__

__post_init__()

Validate that collection method is set when billed entity is not company.

Source code in components/price_provider/internal/entities.py
def __post_init__(self) -> None:
    """Validate that collection method is set when billed entity is not company."""
    if (
        self.billed_entity != BilledEntity.company
        and self.collection_method is None
    ):
        raise ValueError(
            f"Collection method must be set when billed entity is {self.billed_entity}"
        )

amount instance-attribute

amount

The amount for an entire period of service.

Can be negative, for example if the amount corresponds to a discount. Must be expressed in the minor unit of the chosen currency.

beneficiary_type instance-attribute

beneficiary_type

Which member does this price correspond to?

billed_entity instance-attribute

billed_entity

Who is responsible for paying this price?

collection_method class-attribute instance-attribute

collection_method = None

If a member is responsible for paying, how is the payment collected?

compare_component_lists staticmethod

compare_component_lists(left, right)

Compare two lists of price components for equality.

Parameters:

Name Type Description Default
left list[PriceComponent]

First list of components to compare

required
right list[PriceComponent]

Second list of components to compare

required

Returns:

Type Description
bool

True if both lists contain the same components in any order, False otherwise

Source code in components/price_provider/internal/entities.py
@staticmethod
def compare_component_lists(
    left: list["PriceComponent"], right: list["PriceComponent"]
) -> bool:
    """
    Compare two lists of price components for equality.

    Args:
        left: First list of components to compare
        right: Second list of components to compare

    Returns:
        True if both lists contain the same components in any order, False otherwise
    """
    return set(left) == set(right)

contribution_type instance-attribute

contribution_type

What part of the price does this component correspond to?

currency instance-attribute

currency

enrollment_id class-attribute instance-attribute

enrollment_id = None

An optional enrollment ID, in case the component corresponds to an enrolled member. It would typically be set when computing the prices for an existing policy. It would be None when a member projects the cost of adding their partner to their policy.

group_by_beneficiary_id staticmethod

group_by_beneficiary_id(components)

Groups price components by enrollment ID.

Parameters:

Name Type Description Default
components list[PriceComponent]

List of PriceComponent to group

required

Returns:

Type Description
dict[int | UUID, list[PriceComponent]]

Dictionary mapping enrollment ID (int or UUID) to list of components

Raises:

Type Description
ValueError

If any component does not have an enrollment ID

Source code in components/price_provider/internal/entities.py
@staticmethod
def group_by_beneficiary_id(
    components: list["PriceComponent"],
) -> dict[int | UUID, list["PriceComponent"]]:
    """
    Groups price components by enrollment ID.

    Args:
        components: List of PriceComponent to group

    Returns:
        Dictionary mapping enrollment ID (int or UUID) to list of components

    Raises:
        ValueError: If any component does not have an enrollment ID
    """
    grouped: dict[int | UUID, list[PriceComponent]] = {}

    for component in components:
        if component.enrollment_id is None:
            raise ValueError("All components must have an enrollment ID")

        enrollment_id = component.enrollment_id
        if enrollment_id not in grouped:
            grouped[enrollment_id] = []
        grouped[enrollment_id].append(component)

    return grouped

periodicity class-attribute instance-attribute

periodicity = 'monthly'

service_type instance-attribute

service_type

What module does this price correspond to ?

PricingContext dataclass

PricingContext(
    *,
    pricing_source,
    on_date,
    pricing_enrollment_specs,
    rounding_strategy=RoundingStrategy.ARITHMETIC
)

Shared inputs required to compute a price breakdown.

on_date instance-attribute

on_date

pricing_enrollment_specs instance-attribute

pricing_enrollment_specs

pricing_source instance-attribute

pricing_source

Abstract identifier for pricing resolution (policy_id, enrollment_id, etc.).

Note: This field may be deprecated in the future when pricing resolution can be done purely from PricingEnrollmentSpec.modules.

rounding_strategy class-attribute instance-attribute

rounding_strategy = ARITHMETIC

PricingEnrollmentSpec

Bases: Protocol

Represents a member enrollment used for pricing.

enrollment_id property

enrollment_id

Does this spec corresponding to a member enrolled on a policy ?

member_age property

member_age

The age of the member, if available.

member_date_of_birth property

member_date_of_birth

The date of birth of the member, if available

member_type property

member_type

The type of member (primary, partner, child).

modules property

modules

List of enrolled modules to price for this beneficiary.

PricingFunction

Bases: Protocol

Encapsulates price rules. Local implementations are responsible for bringing their own pricing function implementation and resolving pricing sources internally.

get_price_breakdown

get_price_breakdown(pricing_context)

Compute monthly prices for beneficiaries.

Parameters:

Name Type Description Default
pricing_context PricingContext

Inputs required to compute prices, including date, beneficiaries, and rounding strategy.

required

Returns:

Type Description
PriceBreakdown

Price breakdown.

Source code in components/price_provider/internal/entities.py
def get_price_breakdown(self, pricing_context: PricingContext) -> "PriceBreakdown":
    """Compute monthly prices for beneficiaries.

    Args:
        pricing_context: Inputs required to compute prices, including date,
            beneficiaries, and rounding strategy.

    Returns:
        Price breakdown.
    """
    ...

PricingSource module-attribute

PricingSource = str | int | UUID

Abstract identifier for pricing resolution (policy_id, enrollment_id, etc.).

components.price_provider.public.primitives

Shared primitives for pricing.

These are stable, cross-component technical types (currency, rounding strategy, etc.). It is OK to import these broadly.

Currencies

Common currencies supported by this component.

EUR class-attribute instance-attribute

EUR = Currency(
    alphabetic_code="EUR",
    numeric_code=978,
    minor_unit=2,
    symbol="€",
    locale_formats={
        "fr": LocaleFormat(
            decimal_separator=",",
            thousands_separator="\u202f",
            display_format="{amount}\xa0{symbol}",
            negative_sign_position="before_number",
        ),
        "nl": LocaleFormat(
            decimal_separator=",",
            thousands_separator=".",
            display_format="{symbol}\xa0{amount}",
            negative_sign_position="after_symbol",
        ),
        "en": LocaleFormat(
            decimal_separator=".",
            thousands_separator=",",
            display_format="{symbol}{amount}",
            negative_sign_position="before_symbol",
        ),
    },
)

from_alphabetic_code classmethod

from_alphabetic_code(code)

Get a Currency instance by its ISO4217 alphabetic code.

Parameters:

Name Type Description Default
code str

The ISO4217 alphabetic code (e.g., "EUR")

required

Returns:

Type Description
Currency

The Currency instance matching the code

Raises:

Type Description
ValueError

If the currency code is not supported

Examples:

>>> currency = Currencies.from_alphabetic_code("EUR")
>>> currency.alphabetic_code
'EUR'
Source code in components/price_provider/public/primitives.py
@classmethod
def from_alphabetic_code(cls, code: str) -> Currency:
    """
    Get a Currency instance by its ISO4217 alphabetic code.

    Args:
        code: The ISO4217 alphabetic code (e.g., "EUR")

    Returns:
        The Currency instance matching the code

    Raises:
        ValueError: If the currency code is not supported

    Examples:
        >>> currency = Currencies.from_alphabetic_code("EUR")
        >>> currency.alphabetic_code
        'EUR'
    """
    for attr_name in dir(cls):
        if attr_name.startswith("_"):
            continue
        attr_value = getattr(cls, attr_name)
        if isinstance(attr_value, Currency) and (
            attr_value.alphabetic_code == code
        ):
            return attr_value

    supported_codes = [
        getattr(cls, attr).alphabetic_code
        for attr in dir(cls)
        if not attr.startswith("_") and isinstance(getattr(cls, attr), Currency)
    ]
    raise ValueError(
        f"Unsupported currency code: {code}. "
        f"Supported codes: {', '.join(supported_codes)}"
    )

Currency dataclass

Currency(
    *,
    alphabetic_code,
    numeric_code,
    minor_unit,
    symbol,
    locale_formats
)

Represents a currency. Attempting to comply with ISO4217.

__hash__

__hash__()

Hash the currency using its alphabetic code.

The alphabetic code (e.g., "EUR", "USD") uniquely identifies each currency according to ISO4217 standards, making it the ideal hash key.

Source code in components/price_provider/public/primitives.py
def __hash__(self) -> int:
    """
    Hash the currency using its alphabetic code.

    The alphabetic code (e.g., "EUR", "USD") uniquely identifies each
    currency according to ISO4217 standards, making it the ideal hash key.
    """
    return hash(self.alphabetic_code)

alphabetic_code instance-attribute

alphabetic_code

ISO4217 compliant alpha code of the currency.

format_amount

format_amount(amount)

Format an amount in minor units as a string with currency code.

Examples:

formatted = Currencies.EUR.format_amount(1032)
print(formatted) # "10.32 EUR"
Source code in components/price_provider/public/primitives.py
def format_amount(self, amount: int) -> str:
    """Format an amount in minor units as a string with currency code.

    Examples:
        ```python
        formatted = Currencies.EUR.format_amount(1032)
        print(formatted) # "10.32 EUR"
        ```
    """
    decimal_amount = self.from_minor_unit(amount)
    return f"{decimal_amount} {self.alphabetic_code}"

format_amount_with_symbol

format_amount_with_symbol(amount, locale)

Format an amount in minor units with currency symbol for display.

Trailing zeros after the decimal point are removed unless needed. The formatting respects the currency's ISO 4217 minor unit specification. Negative amounts are formatted according to locale-specific conventions.

Parameters:

Name Type Description Default
amount int

The amount in minor units (e.g., cents for EUR)

required
locale str

The locale to use for formatting ("fr", "nl", or "en")

required

Returns:

Type Description
str

Formatted string with locale-specific symbol position and

str

separators

Examples:

# French locale: symbol after with space, comma as decimal, thin
# space as thousands
formatted = Currencies.EUR.format_amount_with_symbol(12020, "fr")
print(formatted)  # "120,20 €"

formatted = Currencies.EUR.format_amount_with_symbol(10000, "fr")
print(formatted)  # "100 €"

formatted = Currencies.EUR.format_amount_with_symbol(-12020, "fr")
print(formatted)  # "-120,20 €"

# English locale: symbol before no space, dot as decimal, comma as
# thousands
formatted = Currencies.EUR.format_amount_with_symbol(12020, "en")
print(formatted)  # "€120.20"

formatted = Currencies.EUR.format_amount_with_symbol(-12020, "en")
print(formatted)  # "-€120.20"

# Dutch locale: symbol before with space, comma as decimal, dot as
# thousands
formatted = Currencies.EUR.format_amount_with_symbol(12020, "nl")
print(formatted)  # "€ 120,20"

formatted = Currencies.EUR.format_amount_with_symbol(-12020, "nl")
print(formatted)  # "€ -120,20"

formatted = Currencies.EUR.format_amount_with_symbol(
    12345678900, "fr"
)
print(formatted)  # "123 456 789 €"
Source code in components/price_provider/public/primitives.py
def format_amount_with_symbol(self, amount: int, locale: str) -> str:
    """Format an amount in minor units with currency symbol for display.

    Trailing zeros after the decimal point are removed unless needed.
    The formatting respects the currency's ISO 4217 minor unit
    specification. Negative amounts are formatted according to
    locale-specific conventions.

    Args:
        amount: The amount in minor units (e.g., cents for EUR)
        locale: The locale to use for formatting ("fr", "nl", or "en")

    Returns:
        Formatted string with locale-specific symbol position and
        separators

    Examples:
        ```python
        # French locale: symbol after with space, comma as decimal, thin
        # space as thousands
        formatted = Currencies.EUR.format_amount_with_symbol(12020, "fr")
        print(formatted)  # "120,20 €"

        formatted = Currencies.EUR.format_amount_with_symbol(10000, "fr")
        print(formatted)  # "100 €"

        formatted = Currencies.EUR.format_amount_with_symbol(-12020, "fr")
        print(formatted)  # "-120,20 €"

        # English locale: symbol before no space, dot as decimal, comma as
        # thousands
        formatted = Currencies.EUR.format_amount_with_symbol(12020, "en")
        print(formatted)  # "€120.20"

        formatted = Currencies.EUR.format_amount_with_symbol(-12020, "en")
        print(formatted)  # "-€120.20"

        # Dutch locale: symbol before with space, comma as decimal, dot as
        # thousands
        formatted = Currencies.EUR.format_amount_with_symbol(12020, "nl")
        print(formatted)  # "€ 120,20"

        formatted = Currencies.EUR.format_amount_with_symbol(-12020, "nl")
        print(formatted)  # "€ -120,20"

        formatted = Currencies.EUR.format_amount_with_symbol(
            12345678900, "fr"
        )
        print(formatted)  # "123 456 789 €"
        ```
    """
    locale_format = self.locale_formats[locale]
    is_negative = amount < 0
    currency_amount = self.from_minor_unit(abs(amount))

    if int(currency_amount) == currency_amount:
        formatted_string = f"{int(currency_amount):,}"
    else:
        formatted_string = f"{currency_amount:,.{self.minor_unit}f}"

    # Use a temporary placeholder to avoid conflicts when thousands and
    # decimal separators are swapped (e.g., Dutch uses "." for thousands
    # and "," for decimals)
    number_part = (
        formatted_string.replace(",", "{{THOUSANDS}}")
        .replace(".", "{{DECIMAL}}")
        .replace("{{THOUSANDS}}", locale_format.thousands_separator)
        .replace("{{DECIMAL}}", locale_format.decimal_separator)
    )

    formatted_result = locale_format.display_format.format(
        amount=number_part, symbol=self.symbol
    )

    if is_negative:
        if locale_format.negative_sign_position == "before_symbol":
            formatted_result = f"-{formatted_result}"
        elif locale_format.negative_sign_position == "after_symbol":
            # Replace "symbol " with "symbol -" to avoid extra space
            formatted_result = formatted_result.replace(
                f"{self.symbol}\u00a0", f"{self.symbol} -", 1
            )
        elif locale_format.negative_sign_position == "before_number":
            formatted_result = formatted_result.replace(
                number_part, f"-{number_part}", 1
            )

    return formatted_result

from_minor_unit

from_minor_unit(amount)

Convert from integer representation of the currency to a decimal representation.

Examples:

decimal_representation = Currencies.EUR.from_minor_unit(1032)
print(decimal_representation) # 10.32
Source code in components/price_provider/public/primitives.py
def from_minor_unit(self, amount: int) -> Decimal:
    """Convert from integer representation of the currency to a decimal
    representation.

    Examples:
        ```python
        decimal_representation = Currencies.EUR.from_minor_unit(1032)
        print(decimal_representation) # 10.32
        ```
    """
    return Decimal(amount) / Decimal(10**self.minor_unit)

locale_formats instance-attribute

locale_formats

Locale-specific formatting configuration for displaying amounts.

Examples:

  • "fr": LocaleFormat(decimal=",", thousands=" ", format="{amount} {symbol}") produces "123 456,78 €"
  • "nl": LocaleFormat(decimal=",", thousands=".", format="{symbol} {amount}") produces "€ 123.456,78"
  • "en": LocaleFormat(decimal=".", thousands=",", format="{symbol}{amount}") produces "€123,456.78"

minor_unit instance-attribute

minor_unit

The number of digits after the decimal separator to represent as the minor unit. Helps us represent amounts as integers instead of floats, for example representing euros as cents.

Certain currenceis are naturally representable as ints and have 0 digits after the decimal place.

Examples: Japanese yen, South Korean won

numeric_code instance-attribute

numeric_code

ISO4217 compliant code of the currency.

symbol instance-attribute

symbol

The currency symbol used for display (e.g., "€", "$", "£").

to_minor_unit

to_minor_unit(amount)

Convert from floating point representation of the currency to an integer representation.

Useful to avoid precision errors of large floats.

Examples:

integer_representation = Currencies.EUR.to_minor_unit(10.32)
print(integer_representation) # 1032 cents
Source code in components/price_provider/public/primitives.py
def to_minor_unit(self, amount: Decimal) -> int:
    """Convert from floating point representation of the currency to an
    integer representation.

    Useful to avoid precision errors of large floats.

    Examples:
        ```python
        integer_representation = Currencies.EUR.to_minor_unit(10.32)
        print(integer_representation) # 1032 cents
        ```
    """
    return int(amount * (10**self.minor_unit))

LocaleFormat dataclass

LocaleFormat(
    *,
    decimal_separator,
    thousands_separator,
    display_format,
    negative_sign_position
)

Configuration for locale-specific number and currency formatting.

decimal_separator instance-attribute

decimal_separator

Character used as decimal separator (e.g., "." or ",")

display_format instance-attribute

display_format

Template for displaying amount with symbol. Should contain {amount} and {symbol} placeholders. Example: "{amount} {symbol}" or "{symbol}{amount}"

negative_sign_position instance-attribute

negative_sign_position

Where to place the minus sign for negative amounts.

Examples:

  • "before_symbol": -€120.20 (English)
  • "after_symbol": € -120,20 (Dutch)
  • "before_number": -120,20 € (French)

thousands_separator instance-attribute

thousands_separator

Character used as thousands separator (e.g., ",", ".", or " ")

RoundingStrategy

Bases: AlanBaseEnum

Strategy for rounding monetary amounts.

ARITHMETIC class-attribute instance-attribute

ARITHMETIC = 'arithmetic'

Round half up (0.5 -> 1). Traditional rounding method.

BANKERS class-attribute instance-attribute

BANKERS = 'bankers'

Round half to even (0.5 -> 0, 1.5 -> 2). Reduces systematic bias.