Skip to content

Api reference

components.core_price.public.adapters

Public concrete CorePriceDependency adapters.

CoreStackCorePriceDependency

Bases: CorePriceDependency

Canonical Core Stack adapter for CorePriceDependency.

Wires the Core Stack pricing context resolver + price breakdown calculator into the abstract CorePriceDependency port. Countries whose pricing fits the Core Stack model can use this directly instead of writing their own.

compute_price_breakdown

compute_price_breakdown(pricing_context, on_date)

Compute a domain PriceBreakdown from a resolved context.

Source code in components/core_price/public/adapters.py
def compute_price_breakdown(
    self, pricing_context: PricingContext, on_date: date
) -> PriceBreakdown:
    """Compute a domain PriceBreakdown from a resolved context."""
    return compute_price_breakdown(pricing_context, on_date)

resolve_pricing_context

resolve_pricing_context(pricing_source, from_date, to_date)

Resolve pricing source into a timeline of pricing contexts.

Source code in components/core_price/public/adapters.py
def resolve_pricing_context(
    self,
    pricing_source: PricingSource,
    from_date: date,
    to_date: date,
) -> Timeline[PricingContext]:
    """Resolve pricing source into a timeline of pricing contexts."""
    return resolve_pricing_context(
        pricing_source=pricing_source,
        from_date=from_date,
        to_date=to_date,
    )

components.core_price.public.api

PlanPricingService

Compute prices without a live user, enrollment, or contract.

Used for plan-level display pricing. Pricing functions are resolved from the insurance plan catalog; collection method defaults to payroll.

get_breakdown_for_plan staticmethod

get_breakdown_for_plan(*, pricing_context)

Compute a price breakdown for a plan given synthetic member specs.

Parameters:

Name Type Description Default
pricing_context PlanPricingContext

Context with synthetic member specs, with module_id = plan_module_id and InputType.member_membership_type set in inputs.

required
Source code in components/core_price/public/api.py
@staticmethod
@deprecated(
    "Only for migrating Spanish use cases before better price-formula representation exists.",
    category=AlanDeprecationWarning,
)
def get_breakdown_for_plan(
    *,
    pricing_context: PlanPricingContext,
) -> PriceBreakdown:
    """Compute a price breakdown for a plan given synthetic member specs.

    Args:
        pricing_context: Context with synthetic member specs, with module_id = plan_module_id
            and InputType.member_membership_type set in inputs.
    """
    from components.core_price.internal.domain.services.calculate_price_for_plan import (
        calculate_price_breakdown_for_plan,
    )

    return calculate_price_breakdown_for_plan(
        pricing_context=pricing_context,
    )

PricingService

Public facade. Business logic lives in internal/domain/services/.

get_breakdown staticmethod

get_breakdown(
    *, on_date: date, pricing_source: PricingSource
) -> PriceBreakdown
get_breakdown(
    *, on_date: date, pricing_context: PricingContext
) -> PriceBreakdown
get_breakdown(
    *,
    on_date: date,
    contract_identifier: ContractIdentifier
) -> PriceBreakdown
get_breakdown(
    *,
    on_date,
    pricing_source=None,
    pricing_context=None,
    contract_identifier=None
)

Compute a price breakdown.

Parameters:

Name Type Description Default
on_date date

Reference date for coverage and affiliation data.

required
pricing_source PricingSource | None

Resolve context from source, then compute.

None
pricing_context PricingContext | None

Pre-built context — caller owns spec composition.

None
contract_identifier ContractIdentifier | None

Currently not implemented.

None
Source code in components/core_price/public/api.py
@staticmethod
def get_breakdown(
    *,
    on_date: date,
    pricing_source: PricingSource | None = None,
    pricing_context: PricingContext | None = None,
    contract_identifier: "ContractIdentifier | None" = None,
) -> "PriceBreakdown":
    """Compute a price breakdown.

    Args:
        on_date: Reference date for coverage and affiliation data.
        pricing_source: Resolve context from source, then compute.
        pricing_context: Pre-built context — caller owns spec composition.
        contract_identifier: Currently not implemented.
    """
    from components.core_price.internal.domain.services.calculate_price import (
        calculate_price_breakdown,
    )

    return calculate_price_breakdown(
        on_date=on_date,
        pricing_source=pricing_source,
        pricing_context=pricing_context,
        contract_identifier=contract_identifier,
    )

get_breakdown_timeline staticmethod

get_breakdown_timeline(
    *, pricing_source, period, compact=False
)

Compute per-(enrollment_group, member, module) PriceBreakdown timelines.

Parameters:

Name Type Description Default
pricing_source PricingSource

Pricing source to resolve into a timeline.

required
period tuple[date, date]

(from_date, to_date) bounds clamping the timeline.

required
compact bool

When True, merge consecutive runs with identical price_components within each per-key timeline, so segments reflect effective price changes only. Defaults to False.

False

Returns:

Type Description
PriceBreakdownTimelines

Dict keyed by (enrollment_group_id, member_id, module_id), each value

PriceBreakdownTimelines

a Timeline of PriceBreakdown effective on contiguous date intervals.

Source code in components/core_price/public/api.py
@staticmethod
def get_breakdown_timeline(
    *,
    pricing_source: PricingSource,
    period: tuple[date, date],
    compact: bool = False,
) -> "PriceBreakdownTimelines":
    """Compute per-(enrollment_group, member, module) PriceBreakdown timelines.

    Args:
        pricing_source: Pricing source to resolve into a timeline.
        period: (from_date, to_date) bounds clamping the timeline.
        compact: When True, merge consecutive runs with identical
            `price_components` within each per-key timeline, so segments
            reflect effective price changes only. Defaults to False.

    Returns:
        Dict keyed by (enrollment_group_id, member_id, module_id), each value
        a Timeline of PriceBreakdown effective on contiguous date intervals.
    """
    from components.core_price.internal.domain.services.timeline_builder import (
        build_breakdown_timeline,
        project_per_group_member_module,
    )
    from components.core_price.public.dependencies import get_app_dependency

    deps = get_app_dependency()
    from_date, to_date = period
    breakdown_timeline = build_breakdown_timeline(
        pricing_source=pricing_source,
        from_date=from_date,
        to_date=to_date,
        resolver=deps,
        calculator=deps,
    )
    return project_per_group_member_module(breakdown_timeline, compact=compact)

resolve_pricing_context staticmethod

resolve_pricing_context(*, pricing_source, on_date)

Return the pricing context effective on on_date.

Raises ValueError if no context is found for the source on that date.

Source code in components/core_price/public/api.py
@staticmethod
def resolve_pricing_context(
    *,
    pricing_source: PricingSource,
    on_date: date,
) -> PricingContext:
    """Return the pricing context effective on on_date.

    Raises ValueError if no context is found for the source on that date.
    """
    from components.core_price.internal.domain.services.resolve_pricing_context import (
        resolve_pricing_context,
    )

    return resolve_pricing_context(pricing_source=pricing_source, on_date=on_date)

components.core_price.public.dependencies

COMPONENT_NAME module-attribute

COMPONENT_NAME = 'core_price'

Canonical name of the core price component.

CorePriceDependency

Bases: PricingContextResolver, PriceBreakdownCalculator, ABC

Public dependency contract for the core price component.

Countries may provide a country-specific implementation, or reuse the canonical Core Stack adapter exposed from components.core_price.public.adapters.

get_app_dependency

get_app_dependency()

Function used to fetch the dependencies from the flask app.

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

Pricing API domain entities.

BePriceComponentType

Bases: PriceComponentType

BE health insurance coverage module. Will move to a proper core-stack definition once the BE plans are migrated

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'

CaPriceComponentType

Bases: PriceComponentType

CA health insurance coverage module.

core_product class-attribute instance-attribute

core_product = 'ca_core_product'

Debtor

Bases: AlanBaseEnum

Payer role used by core_price for price aggregation.

Adapters are responsible for mapping their country-specific payor enum to one of these two values before constructing a PriceComponent.

employer class-attribute instance-attribute

employer = 'employer'

The contracting / sponsoring entity (employer, company).

primary class-attribute instance-attribute

primary = 'primary'

The insured person (primary member, pet owner).

EnrollmentGroupPricingSource dataclass

EnrollmentGroupPricingSource(enrollment_group_id)

Resolve pricing from a core_enrollment enrollment group (core_stack path).

enrollment_group_id instance-attribute

enrollment_group_id

PlanPricingContext dataclass

PlanPricingContext(
    *,
    member_specs,
    rounding_strategy=RoundingStrategy.ARITHMETIC
)

PricingContext used to get a price breakdown from an insurance_plan in spain.

member_specs instance-attribute

member_specs

rounding_strategy class-attribute instance-attribute

rounding_strategy = ARITHMETIC

PolicyPricingSource dataclass

PolicyPricingSource(policy_id)

Resolve pricing from a policy_id directly.

policy_id instance-attribute

policy_id

PriceBreakdown dataclass

PriceBreakdown(*, price_components)

Represents the full price breakdown.

No safeties on price components

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

Source code in components/core_price/internal/domain/entities/price_breakdown.py
def __init__(self, *, price_components: list[PriceComponent]):
    self.price_components = price_components
    self.currency = price_components[0].currency if price_components else None

    # 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 price_components:
        if component.currency != self.currency:
            raise ValueError("Cannot mix different currencies.")

        match component.debtor:
            case Debtor.employer:
                if component.contribution_type == PriceContributionType.taxes:
                    taxes_billed_to_company += component.amount
                else:
                    untaxed_amount_billed_to_company += component.amount
            case Debtor.primary:
                if component.contribution_type == PriceContributionType.taxes:
                    taxes_billed_to_primary += component.amount
                else:
                    untaxed_amount_billed_to_primary += component.amount

    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/core_price/internal/domain/entities/price_breakdown.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)

currency instance-attribute

currency = currency if price_components else None

for_enrollment

for_enrollment(enrollment_id)

Return a new PriceBreakdown containing only components for the given enrollment (legacy path).

Parameters:

Name Type Description Default
enrollment_id int | UUID | None

The enrollment_id identifying the member. Use None for

required
Source code in components/core_price/internal/domain/entities/price_breakdown.py
def for_enrollment(self, enrollment_id: int | UUID | None) -> "PriceBreakdown":
    """Return a new PriceBreakdown containing only components for the given enrollment (legacy path).

    Args:
        enrollment_id: The enrollment_id identifying the member. Use None for
        simulated (not-yet-enrolled) members.
    """
    filtered = [
        c for c in self.price_components if c.enrollment_id == enrollment_id
    ]
    return PriceBreakdown.from_components(filtered)

for_member

for_member(member_id)

Return components for a given member (core_stack path).

Source code in components/core_price/internal/domain/entities/price_breakdown.py
def for_member(self, member_id: str) -> "PriceBreakdown":
    """Return components for a given member (core_stack path)."""
    filtered = [c for c in self.price_components if c.member_id == member_id]
    return PriceBreakdown.from_components(filtered)

for_module

for_module(module_id)

Return components for a given module (core_stack path).

Source code in components/core_price/internal/domain/entities/price_breakdown.py
def for_module(self, module_id: ModuleId) -> "PriceBreakdown":
    """Return components for a given module (core_stack path)."""
    filtered = [c for c in self.price_components if c.module_id == module_id]
    return PriceBreakdown.from_components(filtered)

from_components staticmethod

from_components(price_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/core_price/internal/domain/entities/price_breakdown.py
@staticmethod
def from_components(
    price_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(price_components=price_components)

merge staticmethod

merge(price_breakdowns)

Merge several price breakdowns into a single one.

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

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

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

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

price_components instance-attribute

price_components = price_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
)

PriceBreakdownTimelines module-attribute

PriceBreakdownTimelines = dict[
    PricingTimelineKey, Timeline[PriceBreakdown]
]

PriceComponent dataclass

PriceComponent(
    *,
    service_type,
    contribution_type,
    beneficiary_type,
    debtor,
    collection_method=None,
    member_id=None,
    module_id=None,
    enrollment_group_id=None,
    enrollment_id=None,
    currency,
    amount,
    periodicity="monthly"
)

Contract-enriched price component (one billable line).

Downstream of shared.core_stack.outputs.price_component.PriceComponent (the Plan-side raw output of ModulePricingFunction). Adds Contract data: debtor, collection_method, service_type, beneficiary_type, enrollment_id, periodicity. Plan→Contract mapping happens in the orchestrator's mapping.py.

__post_init__

__post_init__()

Validate that collection method is set when the debtor is not the employer.

Source code in components/core_price/internal/domain/entities/price_breakdown.py
def __post_init__(self) -> None:
    """Validate that collection method is set when the debtor is not the employer."""
    if self.debtor != Debtor.employer and self.collection_method is None:
        raise ValueError(
            f"Collection method must be set when billed entity is {self.debtor}"
        )

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?

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/core_price/internal/domain/entities/price_breakdown.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

debtor instance-attribute

debtor

Who is responsible for paying this price.

enrollment_group_id class-attribute instance-attribute

enrollment_group_id = None

Enrollment group this component belongs to (core_stack path). None for legacy paths.

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 price 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/core_price/internal/domain/entities/price_breakdown.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

member_id class-attribute instance-attribute

member_id = None

Stable member identifier (core_stack path). None for legacy paths.

module_id class-attribute instance-attribute

module_id = None

Module UUID (core_stack path). None for legacy paths. Contains a contract_enrollment_module_id, except for the temporary Spanish get_breakdown_for_plan workaround where it uses plan_module_id.

periodicity class-attribute instance-attribute

periodicity = 'monthly'

service_type instance-attribute

service_type

Which subpart of the service this price covers (country-specific subclass of PriceComponentType, e.g., BePriceComponentType).

PricingContext dataclass

PricingContext(
    *,
    member_specs,
    rounding_strategy=RoundingStrategy.ARITHMETIC,
    enrollment_group_id=None
)

Shared inputs required to compute a price breakdown.

enrollment_group_id class-attribute instance-attribute

enrollment_group_id = None

member_specs instance-attribute

member_specs

primary_spec property

primary_spec

Return the primary member spec.

Uses first instead of one because the BE coverage upgrade flow can inject a simulated primary spec (with member_id=None) via additional_member_specs, resulting in 2 primaries in the list. Real specs always come first (api.py merges policy specs before additional ones). TODO @cfollet: revert to one once upgrade flow uses dedicated pricing endpoint.

rounding_strategy class-attribute instance-attribute

rounding_strategy = ARITHMETIC

with_extra_specs

with_extra_specs(*extra)

Return a copy with additional member specs appended.

Source code in components/core_price/internal/domain/entities/pricing_context.py
def with_extra_specs(self, *extra: "MemberSpec") -> "PricingContext":
    """Return a copy with additional member specs appended."""
    return PricingContext(
        member_specs=[*self.member_specs, *extra],
        rounding_strategy=self.rounding_strategy,
        enrollment_group_id=self.enrollment_group_id,
    )

PricingSource module-attribute

PricingSource = (
    UserPricingSource
    | PolicyPricingSource
    | EnrollmentGroupPricingSource
)

Abstract identifier for pricing resolution.

PricingTimelineKey module-attribute

PricingTimelineKey = tuple[UUID, MemberId, ModuleId]

(enrollment_group_id, member_id, module_id)

UserPricingSource dataclass

UserPricingSource(user_id)

Resolve pricing from a user_id (looks up active enrollment → policy).

user_id instance-attribute

user_id

components.core_price.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.

CAD class-attribute instance-attribute

CAD = Currency(
    alphabetic_code="CAD",
    numeric_code=124,
    minor_unit=2,
    symbol="C$",
    locale_formats={
        "en": LocaleFormat(
            decimal_separator=".",
            thousands_separator=",",
            display_format="{symbol}{amount}",
            negative_sign_position="before_symbol",
        ),
        "fr": LocaleFormat(
            decimal_separator=",",
            thousands_separator="\u202f",
            display_format="{amount}\xa0{symbol}",
            negative_sign_position="before_number",
        ),
    },
)

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",
        ),
        "es": LocaleFormat(
            decimal_separator=",",
            thousands_separator=".",
            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/core_price/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/core_price/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/core_price/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", "en", or "es")

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/core_price/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", "en", or "es")

    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)  # "\u2011120,20\u00a0€"

        # 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)  # "\u2011€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)  # "€\u00a0120,20"

        formatted = Currencies.EUR.format_amount_with_symbol(-12020, "nl")
        print(formatted)  # "€\u00a0\u2011120,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"\u2011{formatted_result}"
        elif locale_format.negative_sign_position == "after_symbol":
            formatted_result = formatted_result.replace(
                f"{self.symbol}\u00a0", f"{self.symbol}\u00a0\u2011", 1
            )
        elif locale_format.negative_sign_position == "before_number":
            formatted_result = formatted_result.replace(
                number_part, f"\u2011{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/core_price/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"
  • "es": LocaleFormat(decimal=",", thousands=".", format="{amount} {symbol}") 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/core_price/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.