Skip to content

Api reference

components.offer_catalog.public.api

OfferCatalogCommand module-attribute

OfferCatalogCommand = CreateOfferFromPayloadCommand[
    str, str
]

get_offer

get_offer(offer_id)

Get an offer by id

Source code in components/offer_catalog/public/api.py
def get_offer(offer_id: int | UUID) -> Offer[TargetPopulation]:
    """Get an offer by id"""
    from components.offer_catalog.public.dependencies import get_app_dependency

    dependency: OfferCatalogDependency[str, str, TargetPopulation] = (
        get_app_dependency()
    )
    return dependency.get_offer(offer_id)

get_offer_for_builder_product_version

get_offer_for_builder_product_version(
    builder_product_version_id, target_population
)

Get an offer by builder product version id

Source code in components/offer_catalog/public/api.py
def get_offer_for_builder_product_version(
    builder_product_version_id: int, target_population: TargetPopulation | None
) -> Offer[TargetPopulation] | None:
    """Get an offer by builder product version id"""
    from components.offer_catalog.public.dependencies import get_app_dependency

    dependency: OfferCatalogDependency[str, str, TargetPopulation] = (
        get_app_dependency()
    )
    return dependency.get_offer_for_builder_product_version(
        builder_product_version_id=builder_product_version_id,
        target_population=target_population,
    )

handle_offer_catalog_command

handle_offer_catalog_command(cmd, commit=True)

Handle a command from the offer catalog component.

Source code in components/offer_catalog/public/api.py
def handle_offer_catalog_command(cmd: OfferCatalogCommand, commit: bool = True) -> None:
    """
    Handle a command from the offer catalog component.
    """
    from components.offer_catalog.internal.command_handlers.create_offer import (
        CreateOfferFromPayloadCommandHandler,
    )

    match cmd:
        case CreateOfferFromPayloadCommand():
            return CreateOfferFromPayloadCommandHandler.handle_command(
                cmd=cmd, commit=commit
            )
        case _:
            raise NotImplementedError(f"Unknown command: {cmd}")

components.offer_catalog.public.blueprint

HealthInsuranceOfferCatalog

Bases: ServerSideAdminToolBlueprint

offer_catalog_api_blueprint module-attribute

offer_catalog_api_blueprint = Blueprint(
    "api/offer_catalog", __name__
)

offer_catalog_api_blueprint_record_once

offer_catalog_api_blueprint_record_once(_)
Source code in components/offer_catalog/public/blueprint.py
@offer_catalog_api_blueprint.record_once
def offer_catalog_api_blueprint_record_once(_: BlueprintSetupState) -> None:  # noqa: D103
    from components.offer_catalog.public.controllers.offer_version import (
        offer_version_endpoint,
    )
    from components.offer_catalog.public.controllers.subscription import (
        subscription_endpoint,
    )

    offer_catalog_api = CustomApi(offer_catalog_api_blueprint)
    offer_catalog_api.add_endpoint(offer_version_endpoint)
    offer_catalog_api.add_endpoint(subscription_endpoint)

    from components.contracting.utils.templating import (  # noqa: ALN039,ALN043 # We want to display the contracting nav bar on the offer catalog pages
        contracting_navigation_bar_content,
    )

    offer_catalog_api_blueprint.add_app_template_global(
        contracting_navigation_bar_content
    )

offer_catalog_blueprint module-attribute

offer_catalog_blueprint = HealthInsuranceOfferCatalog(
    name="offer_catalog",
    import_name=__name__,
    template_folder="../internal/templates",
    static_folder="../internal/static",
)

record_once

record_once(_)
Source code in components/offer_catalog/public/blueprint.py
@offer_catalog_blueprint.record_once
def record_once(_: BlueprintSetupState) -> None:  # noqa: D103
    # Register views
    from components.offer_catalog.internal.views import (  # noqa: F401
        compare_offer_versions,
        list_compliance_violations,
        list_health_offer_versions,
        list_offers,
        list_prevoyance_offer_versions,
        show_offer_version_compliance,
        show_offer_version_overview,
        show_offer_version_pricing,
        show_offer_version_subscriptors,
    )

components.offer_catalog.public.commands

create_offer_from_payload_command

CreateOfferFromPayloadCommand dataclass

CreateOfferFromPayloadCommand(*, payload)

Bases: Generic[PriceTarget, PriceComponentType]

Command requesting to create a new offer from a payload

payload instance-attribute
payload

components.offer_catalog.public.controllers

offer_version

OfferVersionController

Bases: BaseController

offer_version_endpoint module-attribute

offer_version_endpoint = Endpoint('offer_version')

search

search(search_term)
Source code in components/offer_catalog/public/controllers/offer_version.py
@OfferVersionController.action_route(
    "/search/<search_term>",
    methods=["GET"],
    auth_strategy=AuthorizationStrategies.open(),
)
@google_auth_login_required
@obs.api_call()
def search(  # noqa: D103
    search_term: str,
) -> Response:
    from components.offer_catalog.public.offer_version import search_offer_versions

    offer_versions = search_offer_versions(search_term=search_term)

    return make_json_response(
        [
            AutoCompleteFieldData(
                id=str(offer_version.id),
                text=offer_version.friendly_name,
                group=offer_version.offer_name,
            ).to_dict()
            for offer_version in offer_versions
        ]
    )

subscription

SubscriptionController

Bases: BaseController

search

search(search_term)

This controller searches for subscriptions of companies identified by the search term. So we search for companies matching the search terms, then we return the subscriptions of those companies.

Source code in components/offer_catalog/public/controllers/subscription.py
@SubscriptionController.action_route(
    "/search/<search_term>",
    methods=["GET"],
    auth_strategy=AuthorizationStrategies.open(),
)
@google_auth_login_required
@obs.api_call()
def search(
    search_term: str,
) -> Response:
    """
    This controller searches for subscriptions of companies identified by the search term.
    So we search for companies matching the search terms, then we return the subscriptions of those companies.
    """
    from components.fr.internal.business_logic.search import (  # noqa: ALN039,ALN043
        perform_search,
    )
    from components.offer_catalog.external.subscription import paginate_subscriptions

    result = perform_search(
        search_term=search_term,
        search_for_companies=True,
    )

    subscriptions = paginate_subscriptions(
        page=1,
        per_page=1000,
        ends_after=utctoday(),
        subscriptor_refs=[str(company.id) for company in result.companies],
    )

    return make_json_response(
        [
            AutoCompleteFieldData(
                id=str(subscription.id),
                text=subscription.friendly_name,
                group=subscription.contractee.name,
            ).to_dict()
            for subscription in subscriptions.items
        ]
    )

subscription_endpoint module-attribute

subscription_endpoint = Endpoint('subscription')

components.offer_catalog.public.cost

HealthPricesSummary dataclass

HealthPricesSummary(base_price, option_prices)

Price dataclass

Price(
    primary_price,
    partner_price,
    child_price,
    family_price,
    nth_children_free,
    price_structure,
)
child_price instance-attribute
child_price
family_price instance-attribute
family_price
nth_children_free instance-attribute
nth_children_free
partner_price instance-attribute
partner_price
price_structure instance-attribute
price_structure
primary_price instance-attribute
primary_price

base_price instance-attribute

base_price

from_health_prices classmethod

from_health_prices(health_prices)
Source code in components/offer_catalog/public/cost.py
@classmethod
def from_health_prices(cls, health_prices: "HealthPrices") -> "HealthPricesSummary":  # noqa: D102
    return cls(
        base_price=cls.Price(
            primary_price=health_prices.base_price.primary_price,
            partner_price=health_prices.base_price.partner_price,
            child_price=health_prices.base_price.child_price,
            family_price=health_prices.base_price.family_price,
            nth_children_free=health_prices.base_price.nth_children_free,
            price_structure=health_prices.base_price.price_structure,
        ),
        option_prices=[
            cls.Price(
                primary_price=price.primary_price,
                partner_price=price.partner_price,
                child_price=price.child_price,
                family_price=price.family_price,
                nth_children_free=price.nth_children_free,
                price_structure=price.price_structure,
            )
            for price in health_prices.option_prices
        ],
    )

option_prices instance-attribute

option_prices

HealthSubscriptionSettings dataclass

HealthSubscriptionSettings(
    participation_primary,
    participation_partner,
    participation_children,
)

from_settings classmethod

from_settings(settings)
Source code in components/offer_catalog/public/cost.py
@classmethod
def from_settings(cls, settings: dict[str, Any]) -> "HealthSubscriptionSettings":  # noqa: D102
    return cls(
        participation_primary=settings["participation_primary"],
        participation_partner=settings["participation_partner"],
        participation_children=settings["participation_children"],
    )

participation_children instance-attribute

participation_children

participation_partner instance-attribute

participation_partner

participation_primary instance-attribute

participation_primary

get_estimated_cost

get_estimated_cost(
    prices, insights, settings, price_structure=None
)
Source code in components/offer_catalog/public/cost.py
@offer_catalog_tracer_wrap()
def get_estimated_cost(  # noqa: D103
    prices: HealthPricesSummary,
    insights: HealthSubscriptionInsights,
    settings: HealthSubscriptionSettings,
    price_structure: PriceStructureType | None = None,
) -> EstimatedCost | None:
    class UnsupportedNthChildrenFreeError(Exception): ...

    class UnsupportedOptionNumberError(Exception): ...

    def n_children_to_be_paid() -> int:
        if prices.base_price.nth_children_free == 2:
            return insights.n_primaries_with_children
        elif prices.base_price.nth_children_free == 3:
            return insights.n_1st_or_2nd_children
        else:
            raise UnsupportedNthChildrenFreeError()

    def n_children_to_be_paid_options(option_index: int) -> int:
        assert prices.option_prices

        # NOTE: Each option might have a different nth_children_free. Not all the places
        # support that, but here there is not reason not to support it.
        nth_children_free = prices.option_prices[option_index].nth_children_free

        if nth_children_free == 2:
            if option_index == 0:
                return insights.n_primaries_with_children_option
            elif option_index == 1:
                return insights.n_primaries_with_children_option2
            else:
                raise UnsupportedOptionNumberError()
        elif nth_children_free == 3:
            if option_index == 0:
                return insights.n_1st_or_2nd_children_option
            elif option_index == 1:
                return insights.n_1st_or_2nd_children_option2
            else:
                raise UnsupportedOptionNumberError()
        else:
            raise UnsupportedNthChildrenFreeError()

    try:
        primary_participation = settings.participation_primary
        partner_participation = settings.participation_partner
        children_participation = settings.participation_children

        return EstimatedCost(
            subscription_insights=insights,
            base_cost=EstimatedCostLine(
                primary_cost=EstimatedCostLine.CostDetails(
                    total=insights.n_live_primaries * prices.base_price.primary_price,
                    company_participation=primary_participation,
                ),
                partner_cost=(
                    EstimatedCostLine.CostDetails(
                        total=insights.n_live_partners
                        * prices.base_price.partner_price,
                        company_participation=partner_participation,
                    )
                    if prices.base_price.partner_price
                    else None
                ),
                children_cost=(
                    EstimatedCostLine.CostDetails(
                        total=n_children_to_be_paid() * prices.base_price.child_price,
                        company_participation=children_participation,
                    )
                    if prices.base_price.child_price
                    else None
                ),
                family_cost=(
                    EstimatedCostLine.CostDetails(
                        total=insights.n_live_families * prices.base_price.family_price,
                        company_participation=children_participation,
                    )
                    if prices.base_price.family_price
                    else None
                ),
                price_structure=price_structure,
            ),
            option_costs=[
                EstimatedCostLine(
                    primary_cost=EstimatedCostLine.CostDetails(
                        total=insights.n_live_primaries_option * price.primary_price,
                        company_participation=0,
                    ),
                    partner_cost=(
                        EstimatedCostLine.CostDetails(
                            total=insights.n_live_partners_option * price.partner_price,
                            company_participation=0,
                        )
                        if price.partner_price
                        else None
                    ),
                    children_cost=(
                        EstimatedCostLine.CostDetails(
                            total=n_children_to_be_paid_options(i) * price.child_price,
                            company_participation=0,
                        )
                        if price.child_price
                        else None
                    ),
                    family_cost=(
                        EstimatedCostLine.CostDetails(
                            total=insights.n_live_families_option * price.family_price,
                            company_participation=0,
                        )
                        if price.family_price
                        else None
                    ),
                    price_structure=price_structure,
                )
                for i, price in enumerate(prices.option_prices)
            ],
        )
    except UnsupportedNthChildrenFreeError:
        current_logger.warning("Don't support nth_children_free != 2 or != 3")
        return None
    except UnsupportedOptionNumberError:
        current_logger.warning("Don't support more than 2 options")
        return None

get_estimated_cost_from_period

get_estimated_cost_from_period(
    subscription_period, offer_version_id
)

Estimate the cost of a subscription period on a given date If offer version is provided, we use it to compute the prices instead of the subscription period's one

Source code in components/offer_catalog/public/cost.py
def get_estimated_cost_from_period(
    subscription_period: SubscriptionPeriod,
    offer_version_id: Optional[str],
) -> EstimatedCost | None:
    """
    Estimate the cost of a subscription period on a given date
    If offer version is provided, we use it to compute the prices instead of the subscription period's one
    """
    from components.offer_catalog.external.insights import get_metrics_per_subscriptions

    subscription_insights = one_or_none(
        get_metrics_per_subscriptions(
            account_refs=[],
            subscription_refs=[subscription_period.subscription_id],
        )
    )

    if offer_version_id:
        offer_version = get_offer_version(offer_version_id=offer_version_id)
    else:
        offer_version = get_offer_version(
            offer_version_id=subscription_period.product.id
        )

    if not subscription_insights:
        return None

    return get_estimated_cost(
        prices=HealthPricesSummary.from_health_prices(
            health_prices=offer_version.prices
        ),
        insights=HealthSubscriptionInsights.from_metrics(metrics=subscription_insights),
        settings=HealthSubscriptionSettings.from_settings(
            settings=subscription_period.settings
        ),
    )

list_estimated_cost_periods

list_estimated_cost_periods(subscription_periods)

Returns: list of PeriodEstimatedCost for each subscription period of type health_insurance. For now, we exclude prevoyance subscription.

Source code in components/offer_catalog/public/cost.py
@offer_catalog_tracer_wrap()
def list_estimated_cost_periods(
    subscription_periods: list[SubscriptionPeriod],
) -> list[PeriodEstimatedCost]:
    """

    Returns: list of PeriodEstimatedCost for each subscription period of type health_insurance.
    For now, we exclude prevoyance subscription.

    """
    from components.offer_catalog.external.insights import get_metrics_per_subscriptions

    subscription_insights = get_metrics_per_subscriptions(
        subscription_refs=list(
            {period.subscription_id for period in subscription_periods}
        ),
    )
    insight_by_subscription_ref = {
        entity.subscription_ref: entity for entity in subscription_insights
    }
    offer_version_ids = uniquify([period.product.id for period in subscription_periods])

    offer_versions_by_id = {
        str(offer_version.id): offer_version
        for offer_version in get_offer_versions(offer_version_ids)
    }

    estimated_costs = []

    for period in subscription_periods:
        if period.subscription_type == SubscriptionType.health_insurance:
            offer_version = offer_versions_by_id.get(period.product.id)
            insights = insight_by_subscription_ref.get(period.subscription_id)

            if offer_version and insights:
                estimated_cost = get_estimated_cost(
                    prices=HealthPricesSummary.from_health_prices(
                        health_prices=offer_version.prices
                    ),
                    insights=HealthSubscriptionInsights.from_metrics(metrics=insights),
                    settings=HealthSubscriptionSettings.from_settings(
                        settings=period.settings
                    ),
                )
                if estimated_cost:
                    estimated_costs.append(
                        PeriodEstimatedCost(
                            period_id=period.id,
                            estimated_cost=estimated_cost,
                        )
                    )

    return estimated_costs

components.offer_catalog.public.dependencies

COMPONENT_NAME module-attribute

COMPONENT_NAME = 'offer_catalog'

OfferCatalogDependency

Bases: ABC, Generic[PriceTarget, PriceComponentType, TargetPopulation]

Abstract class defining the dependencies needed by the Offer Catalog component.

create_offer abstractmethod

create_offer(guarantee_catalog, payload)

Create an offer from a payload.

Returns the created offer.

Warning: Given the Offer objects are country specific, the global component doesn't know the type of returned object. So this method should take care of adding the objects to the DB session when needed (but not committing as this is the responsibility of the command handler).

Source code in components/offer_catalog/public/dependencies.py
@abstractmethod
def create_offer(
    self,
    guarantee_catalog: GuaranteeCatalog,
    payload: OfferPayload[PriceTarget, PriceComponentType],
) -> Offer[TargetPopulation]:
    """
    Create an offer from a payload.

    Returns the created offer.

    Warning: Given the Offer objects are country specific, the global
    component doesn't know the type of returned object. So this method should
    take care of adding the objects to the DB session when needed (but not
    committing as this is the responsibility of the command handler).
    """
    pass

get_country_code abstractmethod

get_country_code()

Returns the country code

Source code in components/offer_catalog/public/dependencies.py
@abstractmethod
def get_country_code(self) -> str:
    """Returns the country code"""
    pass

get_offer abstractmethod

get_offer(offer_id)

Get an offer by id

Source code in components/offer_catalog/public/dependencies.py
@abstractmethod
def get_offer(self, offer_id: int | UUID) -> Offer[TargetPopulation]:
    """Get an offer by id"""
    pass

get_offer_for_builder_product_version abstractmethod

get_offer_for_builder_product_version(
    builder_product_version_id, target_population
)

Returns the offers for a builder product version.

Source code in components/offer_catalog/public/dependencies.py
@abstractmethod
def get_offer_for_builder_product_version(
    self,
    builder_product_version_id: int,
    target_population: TargetPopulation | None,
) -> Offer[TargetPopulation] | None:
    """
    Returns the offers for a builder product version.
    """
    pass

get_app_dependency

get_app_dependency()

Retrieve the Offer Catalog dependency from the current app.

Source code in components/offer_catalog/public/dependencies.py
def get_app_dependency() -> OfferCatalogDependency[
    PriceTarget, PriceComponentType, TargetPopulation
]:
    """Retrieve the Offer Catalog dependency from the current app."""
    from flask import current_app

    return cast(
        "OfferCatalogDependency[PriceTarget, PriceComponentType, TargetPopulation]",
        cast("CustomFlask", current_app).get_component_dependency(COMPONENT_NAME),
    )

set_app_dependency

set_app_dependency(dependency)

Set the Offer Catalog dependency of the current app.

Source code in components/offer_catalog/public/dependencies.py
def set_app_dependency(
    dependency: OfferCatalogDependency[
        PriceTarget, PriceComponentType, TargetPopulation
    ],
) -> None:
    """Set the Offer Catalog dependency of the current app."""
    from flask import current_app

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

components.offer_catalog.public.entities

for_display

BuilderProductVersion dataclass

BuilderProductVersion(
    id, version_number, builder_product_id
)
builder_product_id instance-attribute
builder_product_id
id instance-attribute
id
version_number instance-attribute
version_number

EstimatedCost dataclass

EstimatedCost(
    subscription_insights, base_cost, option_costs
)
base_cost instance-attribute
base_cost
compare
compare(other)
Source code in components/offer_catalog/public/entities/for_display.py
def compare(self, other: "EstimatedCost") -> EstimatedCostComparison:  # noqa: D102
    return EstimatedCostComparison(
        total_cost_diff_in_euros=(other.total - self.total) / 100,
        total_cost_diff_in_percent=(
            (other.total - self.total) / self.total * 100 if self.total else 0
        ),
        company_cost_diff_in_euros=(
            other.total_company_part - self.total_company_part
        )
        / 100,
        company_cost_diff_in_percent=(
            (other.total_company_part - self.total_company_part)
            / self.total_company_part
            * 100
            if self.total_company_part
            else 0
        ),
        employee_cost_diff_in_euros=(
            other.total_employee_part - self.total_employee_part
        )
        / 100,
        employee_cost_diff_in_percent=(
            (other.total_employee_part - self.total_employee_part)
            / self.total_employee_part
            * 100
            if self.total_employee_part
            else 0
        ),
    )
option_costs instance-attribute
option_costs
subscription_insights instance-attribute
subscription_insights
total property
total
total_company_part property
total_company_part
total_employee_part property
total_employee_part

EstimatedCostComparison dataclass

EstimatedCostComparison(
    total_cost_diff_in_euros,
    total_cost_diff_in_percent,
    company_cost_diff_in_euros,
    company_cost_diff_in_percent,
    employee_cost_diff_in_euros,
    employee_cost_diff_in_percent,
)
company_cost_diff_in_euros instance-attribute
company_cost_diff_in_euros
company_cost_diff_in_percent instance-attribute
company_cost_diff_in_percent
employee_cost_diff_in_euros instance-attribute
employee_cost_diff_in_euros
employee_cost_diff_in_percent instance-attribute
employee_cost_diff_in_percent
total_cost_diff_in_euros instance-attribute
total_cost_diff_in_euros
total_cost_diff_in_percent instance-attribute
total_cost_diff_in_percent

EstimatedCostLine dataclass

EstimatedCostLine(
    primary_cost,
    partner_cost,
    children_cost,
    family_cost,
    price_structure,
)
CostDetails dataclass
CostDetails(total, company_participation)
__post_init__
__post_init__()
Source code in components/offer_catalog/public/entities/for_display.py
def __post_init__(self):  # type: ignore[no-untyped-def]  # noqa: D105
    # Approx because cost are rounded
    if self.total - (self.company_part + self.employee_part) > 10:
        raise ValueError(
            "Total cost must be almost equal to company + employee"
        )
company_part property
company_part
company_participation instance-attribute
company_participation
employee_part property
employee_part
employee_participation property
employee_participation
total instance-attribute
total
__post_init__
__post_init__()
Source code in components/offer_catalog/public/entities/for_display.py
def __post_init__(self):  # type: ignore[no-untyped-def]  # noqa: D105
    # Price structure primary_duo_family is a special case only supported for competitor products
    if (
        self.price_structure != PriceStructureType.primary_duo_family
        and self.family_cost is not None
        and (self.partner_cost is not None or self.children_cost is not None)
    ):
        raise ValueError(
            "family_cost should be 0 if partner_cost or children_cost is greater than 0"
        )
children_cost instance-attribute
children_cost
compare
compare(other)
Source code in components/offer_catalog/public/entities/for_display.py
def compare(self, other: "EstimatedCostLine") -> "EstimatedCostComparison":  # noqa: D102
    return EstimatedCostComparison(
        total_cost_diff_in_euros=(other.total - self.total) / 100,
        total_cost_diff_in_percent=(
            (other.total - self.total) / self.total * 100 if self.total else 0
        ),
        company_cost_diff_in_euros=(
            other.total_company_part - self.total_company_part
        )
        / 100,
        company_cost_diff_in_percent=(
            (other.total_company_part - self.total_company_part)
            / self.total_company_part
            * 100
            if self.total_company_part
            else 0
        ),
        employee_cost_diff_in_euros=(
            other.total_employee_part - self.total_employee_part
        )
        / 100,
        employee_cost_diff_in_percent=(
            (other.total_employee_part - self.total_employee_part)
            / self.total_employee_part
            * 100
            if self.total_employee_part
            else 0
        ),
    )
family_cost instance-attribute
family_cost
is_isole_famille property
is_isole_famille
partner_cost instance-attribute
partner_cost
price_structure instance-attribute
price_structure
primary_cost instance-attribute
primary_cost
total property
total
total_company_part property
total_company_part
total_employee_part property
total_employee_part

HealthSubscriptionInsights dataclass

HealthSubscriptionInsights(
    n_primaries_with_children,
    n_primaries_with_children_option,
    n_primaries_with_children_option2,
    n_1st_or_2nd_children,
    n_1st_or_2nd_children_option,
    n_1st_or_2nd_children_option2,
    n_live_primaries,
    n_live_partners,
    n_live_families,
    n_live_primaries_option,
    n_live_partners_option,
    n_live_families_option,
)
from_metrics classmethod
from_metrics(metrics)
Source code in components/offer_catalog/public/entities/for_display.py
@classmethod
def from_metrics(  # noqa: D102
    cls, metrics: "MetricsPerSubscriptionEntity"
) -> "HealthSubscriptionInsights":
    return cls(
        n_primaries_with_children=metrics.n_primaries_with_children,
        n_primaries_with_children_option=metrics.n_primaries_with_children_option,
        n_primaries_with_children_option2=metrics.n_primaries_with_children_option2,
        n_1st_or_2nd_children=metrics.n_1st_or_2nd_children,
        n_1st_or_2nd_children_option=metrics.n_1st_or_2nd_children_option,
        n_1st_or_2nd_children_option2=metrics.n_1st_or_2nd_children_option2,
        n_live_primaries=metrics.n_live_primaries,
        n_live_partners=metrics.n_live_partners,
        n_live_families=metrics.n_live_families,
        n_live_primaries_option=metrics.n_live_primaries_option,
        n_live_partners_option=metrics.n_live_partners_option,
        n_live_families_option=metrics.n_live_families_option,
    )
n_1st_or_2nd_children instance-attribute
n_1st_or_2nd_children
n_1st_or_2nd_children_option instance-attribute
n_1st_or_2nd_children_option
n_1st_or_2nd_children_option2 instance-attribute
n_1st_or_2nd_children_option2
n_live_families instance-attribute
n_live_families
n_live_families_option instance-attribute
n_live_families_option
n_live_partners instance-attribute
n_live_partners
n_live_partners_option instance-attribute
n_live_partners_option
n_live_primaries instance-attribute
n_live_primaries
n_live_primaries_option instance-attribute
n_live_primaries_option
n_primaries_with_children instance-attribute
n_primaries_with_children
n_primaries_with_children_option instance-attribute
n_primaries_with_children_option
n_primaries_with_children_option2 instance-attribute
n_primaries_with_children_option2

OfferCategory

Bases: AlanBaseEnum

health class-attribute instance-attribute
health = 'health'
prevoyance class-attribute instance-attribute
prevoyance = 'prevoyance'

OfferCriterionName

Bases: SearchCriterionName

segment class-attribute instance-attribute
segment = 'segment'
type class-attribute instance-attribute
type = 'type'

OfferVersion dataclass

OfferVersion(
    id,
    bundle_version,
    emoji_code,
    offer_name,
    professional_category,
    start_date,
    category,
    visibility,
    document_version,
    type,
    number_of_options,
    prices,
    builder_product_version=None,
)
builder_product_version class-attribute instance-attribute
builder_product_version = None
bundle_version instance-attribute
bundle_version
category instance-attribute
category
document_version instance-attribute
document_version
emoji_code instance-attribute
emoji_code
friendly_name property
friendly_name
id instance-attribute
id
number_of_options instance-attribute
number_of_options
offer_name instance-attribute
offer_name
prices instance-attribute
prices
professional_category instance-attribute
professional_category
start_date instance-attribute
start_date
type instance-attribute
type
visibility instance-attribute
visibility

OfferVersionCriterionName

Bases: SearchCriterionName

active class-attribute instance-attribute
active = 'Active'
bundle_version class-attribute instance-attribute
bundle_version = 'Bundle version'
get_criterion_parameters classmethod
get_criterion_parameters()
Source code in components/offer_catalog/public/entities/for_display.py
@classmethod
def get_criterion_parameters(  # noqa: D102
    cls,
) -> dict["SearchCriterionName", SearchCriterionParameter]:
    return {
        OfferVersionCriterionName.offer_name: SearchCriterionParameter(
            is_advanced=False,
        ),
        OfferVersionCriterionName.type: SearchCriterionParameter(
            is_advanced=False,
        ),
        OfferVersionCriterionName.professional_category: SearchCriterionParameter(
            is_advanced=True,
        ),
        OfferVersionCriterionName.start_month: SearchCriterionParameter(
            is_advanced=True,
        ),
        OfferVersionCriterionName.visibility: SearchCriterionParameter(
            is_advanced=True,
        ),
        OfferVersionCriterionName.number_of_options: SearchCriterionParameter(
            is_advanced=True,
        ),
        OfferVersionCriterionName.active: SearchCriterionParameter(
            is_advanced=True,
        ),
        OfferVersionCriterionName.bundle_version: SearchCriterionParameter(
            is_advanced=False,
        ),
    }
number_of_options class-attribute instance-attribute
number_of_options = 'Number of options'
offer_name class-attribute instance-attribute
offer_name = 'Offer name'
professional_category class-attribute instance-attribute
professional_category = 'Professional category'
start_month class-attribute instance-attribute
start_month = 'Start month'
type class-attribute instance-attribute
type = 'Type'
visibility class-attribute instance-attribute
visibility = 'Visibility'

PeriodEstimatedCost dataclass

PeriodEstimatedCost(period_id, estimated_cost)
estimated_cost instance-attribute
estimated_cost
period_id instance-attribute
period_id

PrevoyanceOfferVersionCriterionName

Bases: SearchCriterionName

ccn class-attribute instance-attribute
ccn = 'CCN'
ccn_code_only class-attribute instance-attribute
ccn_code_only = 'CCN Code'
professional_category class-attribute instance-attribute
professional_category = 'Professional category'

SearcheableOfferItem dataclass

SearcheableOfferItem(name, segment, type, bundle_versions)

Bases: SearcheableItem[OfferCriterionName]

bundle_versions instance-attribute
bundle_versions
get_search_criteria
get_search_criteria()
Source code in components/offer_catalog/public/entities/for_display.py
def get_search_criteria(self) -> SearchCriteria[OfferCriterionName]:  # noqa: D102
    return SearchCriteria(
        [
            SearchCriterion(OfferCriterionName.segment, {self.segment.value}),
            SearchCriterion(OfferCriterionName.type, {self.type.value}),
        ]
    )
get_search_keywords
get_search_keywords()
Source code in components/offer_catalog/public/entities/for_display.py
def get_search_keywords(self) -> list[str]:  # noqa: D102
    return []
name instance-attribute
name
segment instance-attribute
segment
type instance-attribute
type

SearcheableOfferVersionItem dataclass

SearcheableOfferVersionItem(
    id,
    bundle_version,
    emoji_code,
    offer_name,
    type,
    professional_category,
    start_date,
    visibility,
    number_of_options,
    active,
    closed_contract_count,
    open_contract_count,
    open_policy_count,
)

Bases: SearcheableItem[OfferVersionCriterionName]

active instance-attribute
active
bundle_version instance-attribute
bundle_version
closed_contract_count instance-attribute
closed_contract_count
emoji_code instance-attribute
emoji_code
get_search_criteria
get_search_criteria()
Source code in components/offer_catalog/public/entities/for_display.py
def get_search_criteria(self) -> SearchCriteria[OfferVersionCriterionName]:  # noqa: D102
    return SearchCriteria(
        [
            SearchCriterion(
                name=OfferVersionCriterionName.offer_name,
                values={self.offer_name},
            ),
            SearchCriterion(
                name=OfferVersionCriterionName.type,
                values={self.type.value},
            ),
            SearchCriterion(
                name=OfferVersionCriterionName.professional_category,
                values={self.professional_category.value},
            ),
            SearchCriterion(
                name=OfferVersionCriterionName.start_month,
                values={self.start_date.strftime("%Y-%m")},
            ),
            SearchCriterion(
                name=OfferVersionCriterionName.visibility,
                values={self.visibility},
            ),
            SearchCriterion(
                name=OfferVersionCriterionName.number_of_options,
                values={str(self.number_of_options)},
            ),
            SearchCriterion(
                name=OfferVersionCriterionName.active,
                values={"Yes" if self.active else "No"},
            ),
            SearchCriterion(
                name=OfferVersionCriterionName.bundle_version,
                values={self.bundle_version},
            ),
        ]
    )
get_search_keywords
get_search_keywords()
Source code in components/offer_catalog/public/entities/for_display.py
def get_search_keywords(self) -> list[str]:  # noqa: D102
    return []
id instance-attribute
id
number_of_options instance-attribute
number_of_options
offer_name instance-attribute
offer_name
open_contract_count instance-attribute
open_contract_count
open_policy_count instance-attribute
open_policy_count
professional_category instance-attribute
professional_category
start_date instance-attribute
start_date
type instance-attribute
type
visibility instance-attribute
visibility

SearcheablePrevoyanceOfferVersionItem dataclass

SearcheablePrevoyanceOfferVersionItem(
    id,
    insurer_name,
    cnp_code,
    cnp_libelle,
    price_ta,
    price_tb,
    price_tc,
    professional_category,
    ccn_codes,
    ccn_codes_only,
    created_at,
    closed_contract_count,
    open_contract_count,
    eligible_employee_count,
)

Bases: SearcheableItem[PrevoyanceOfferVersionCriterionName]

ccn_codes instance-attribute
ccn_codes
ccn_codes_only instance-attribute
ccn_codes_only
closed_contract_count instance-attribute
closed_contract_count
cnp_code instance-attribute
cnp_code
cnp_libelle instance-attribute
cnp_libelle
created_at instance-attribute
created_at
eligible_employee_count instance-attribute
eligible_employee_count
get_search_criteria
get_search_criteria()
Source code in components/offer_catalog/public/entities/for_display.py
def get_search_criteria(  # noqa: D102
    self,
) -> SearchCriteria[PrevoyanceOfferVersionCriterionName]:
    return SearchCriteria(
        [
            SearchCriterion(  # represents the full CCN libelle. Eg: "0018 - Industrie de la bonneterie et des industries annexes"
                PrevoyanceOfferVersionCriterionName.ccn,
                self.ccn_codes,
            ),
            SearchCriterion(  # represents the code of the CCN. Eg: "0018"
                PrevoyanceOfferVersionCriterionName.ccn_code_only,
                self.ccn_codes_only,
            ),
            SearchCriterion(
                PrevoyanceOfferVersionCriterionName.professional_category,
                {self.professional_category.value},
            ),
        ]
    )
get_search_keywords
get_search_keywords()
Source code in components/offer_catalog/public/entities/for_display.py
def get_search_keywords(self) -> list[str]:  # noqa: D102
    return []
id instance-attribute
id
insurer_name instance-attribute
insurer_name
open_contract_count instance-attribute
open_contract_count
price_ta instance-attribute
price_ta
price_tb instance-attribute
price_tb
price_tc instance-attribute
price_tc
professional_category instance-attribute
professional_category

guarantee_parameters

AggregatedLimitParameter module-attribute

AggregatedLimitParameter = (
    CategoryParameter | BundleChoiceParameter
)

BundleChoiceParameter dataclass

BundleChoiceParameter(*, type, value, bundle_choice_ref)

Bases: CoverageRuleParameter

A bundle choice parameter and its associated value

bundle_choice_ref instance-attribute
bundle_choice_ref

CategoryParameter dataclass

CategoryParameter(*, type, value, category_ref)

Bases: CoverageRuleParameter

A category parameter and its associated value

category_ref instance-attribute
category_ref

CoverageRuleParameter dataclass

CoverageRuleParameter(*, type, value)

Bases: DataClassJsonMixin

A coverage rule parameter and its associated value

type instance-attribute
type
value instance-attribute
value

PropagatedParameter module-attribute

PropagatedParameter = (
    CategoryParameter | BundleChoiceParameter
)

offer

Offer dataclass

Offer(*, id, bundle_version, target_population)

Bases: DataClassJsonMixin, Generic[TargetPopulation]

An offer, ie an item that Alan can propose to a client.

bundle_version instance-attribute
bundle_version
id instance-attribute
id
target_population instance-attribute
target_population

TargetPopulation module-attribute

TargetPopulation = TypeVar('TargetPopulation', bound=str)

offer_payload

OfferCoverage dataclass

OfferCoverage(
    *,
    name,
    coverage_index,
    coverage_type,
    selected_categories,
    coverage_rules,
    propagated_parameters,
    aggregated_limit_parameters,
    price_grids,
    country_specific_data=None
)

Bases: DataClassJsonMixin, Generic[PriceTarget, PriceComponentType]

A fully defined coverage

aggregated_limit_parameters instance-attribute
aggregated_limit_parameters
country_specific_data class-attribute instance-attribute
country_specific_data = None
coverage_index instance-attribute
coverage_index
coverage_rules instance-attribute
coverage_rules
coverage_type instance-attribute
coverage_type
name instance-attribute
name
price_grids instance-attribute
price_grids
propagated_parameters instance-attribute
propagated_parameters
selected_categories instance-attribute
selected_categories

OfferCoverageRule dataclass

OfferCoverageRule(
    *,
    guarantee_ref,
    bundle_choice_ref,
    expression_type,
    parameters,
    eligibility_items_refs
)

Bases: DataClassJsonMixin

A coverage rule with associated parameters, eligibility items and cost estimates

bundle_choice_ref instance-attribute
bundle_choice_ref
eligibility_items_refs instance-attribute
eligibility_items_refs
expression_type instance-attribute
expression_type
guarantee_ref instance-attribute
guarantee_ref
parameters instance-attribute
parameters

OfferPayload dataclass

OfferPayload(*, coverages, country_specific_data)

Bases: DataClassJsonMixin, Generic[PriceTarget, PriceComponentType]

Command requesting to create a new offer

country_specific_data instance-attribute
country_specific_data
coverages instance-attribute
coverages

OfferPriceGrid dataclass

OfferPriceGrid(
    *,
    target,
    component_type,
    tax_rate,
    nth_children_free,
    max_child_age,
    price_rules
)

Bases: DataClassJsonMixin, Generic[PriceTarget, PriceComponentType]

A price_grid for a target and component_type

component_type instance-attribute
component_type
max_child_age instance-attribute
max_child_age
nth_children_free instance-attribute
nth_children_free
price_rules instance-attribute
price_rules
target instance-attribute
target
tax_rate instance-attribute
tax_rate

OfferPriceRule dataclass

OfferPriceRule(
    *,
    min_age,
    max_age=None,
    primary_cents=0,
    partner_cents=0,
    child_cents=0,
    family_cents=0,
    primary_membership_fee_cents=0,
    partner_membership_fee_cents=0,
    child_membership_fee_cents=0,
    family_membership_fee_cents=0
)

Bases: DataClassJsonMixin

A price_rule, ie prices per enrollment_type and age_bracket

__add__
__add__(other)

Add two price_rules into a new price_rule

Source code in components/offer_catalog/public/entities/offer_payload.py
def __add__(self, other: OfferPriceRule) -> OfferPriceRule:
    """Add two price_rules into a new price_rule"""
    if self.min_age != other.min_age or self.max_age != other.max_age:
        raise ValueError("Can't sum price_rules with different age_brackets")
    return OfferPriceRule(
        min_age=self.min_age,
        max_age=self.max_age,
        primary_cents=self.primary_cents + other.primary_cents,
        partner_cents=self.partner_cents + other.partner_cents,
        child_cents=self.child_cents + other.child_cents,
        family_cents=self.family_cents + other.family_cents,
        primary_membership_fee_cents=self.primary_membership_fee_cents
        + other.primary_membership_fee_cents,
        partner_membership_fee_cents=self.partner_membership_fee_cents
        + other.partner_membership_fee_cents,
        child_membership_fee_cents=self.child_membership_fee_cents
        + other.child_membership_fee_cents,
        family_membership_fee_cents=self.family_membership_fee_cents
        + other.family_membership_fee_cents,
    )
child_cents class-attribute instance-attribute
child_cents = 0
child_membership_fee_cents class-attribute instance-attribute
child_membership_fee_cents = 0
family_cents class-attribute instance-attribute
family_cents = 0
family_membership_fee_cents class-attribute instance-attribute
family_membership_fee_cents = 0
max_age class-attribute instance-attribute
max_age = None
min_age instance-attribute
min_age
partner_cents class-attribute instance-attribute
partner_cents = 0
partner_membership_fee_cents class-attribute instance-attribute
partner_membership_fee_cents = 0
primary_cents class-attribute instance-attribute
primary_cents = 0
primary_membership_fee_cents class-attribute instance-attribute
primary_membership_fee_cents = 0

PriceComponentType module-attribute

PriceComponentType = TypeVar(
    "PriceComponentType", bound=str
)

PriceTarget module-attribute

PriceTarget = TypeVar('PriceTarget', bound=str)

components.offer_catalog.public.offer

paginate_offers

paginate_offers(filters, page=1, per_page=1000)
Source code in components/offer_catalog/public/offer.py
def paginate_offers(  # noqa: D103
    filters: Filters[OfferCriterionName],
    page: int = 1,
    per_page: int = 1000,
) -> FilterResult[OfferCriterionName, SearcheableOfferItem]:
    from components.offer_catalog.external.offer import get_all_offers

    # NOTE: This might be a bit expensive in terms of memory. Let's see.
    all_offers = get_all_offers()

    filter_criteria = filters.search_criteria
    filtered_offers = [
        offer
        for offer in all_offers
        if offer.get_search_criteria().matches(filter_criteria)
    ]

    return FilterResult.from_full_list(  # type: ignore[no-any-return]
        items=filtered_offers,
        page=page,
        per_page=per_page,
        all_available_items=list(all_offers),
        filters=filters,
    )

components.offer_catalog.public.offer_version

get_offer_version

get_offer_version(
    offer_version_id, offer_category=OfferCategory.health
)
Source code in components/offer_catalog/public/offer_version.py
def get_offer_version(  # noqa: D103
    offer_version_id: str, offer_category: OfferCategory = OfferCategory.health
) -> OfferVersion:
    if offer_category == OfferCategory.health:
        from components.offer_catalog.external.health_offer_version import (
            get_offer_versions as external_get_offer_versions,
        )

        return one(external_get_offer_versions(offer_version_id))
    elif offer_category == OfferCategory.prevoyance:
        from components.offer_catalog.external.prevoyance_offer_version import (
            get_offer_versions as external_get_offer_versions,
        )

        return one(external_get_offer_versions(offer_version_id))
    else:
        raise Exception(f"Unknown offer category: {offer_category}")

get_offer_versions

get_offer_versions(
    offer_version_ids, offer_category=OfferCategory.health
)
Source code in components/offer_catalog/public/offer_version.py
def get_offer_versions(  # noqa: D103
    offer_version_ids: list[str],
    offer_category: OfferCategory = OfferCategory.health,
) -> list[OfferVersion]:
    if offer_category == OfferCategory.health:
        from components.offer_catalog.external.health_offer_version import (
            get_offer_versions as external_get_offer_versions,
        )

        return external_get_offer_versions(*offer_version_ids)
    elif offer_category == OfferCategory.prevoyance:
        from components.offer_catalog.external.prevoyance_offer_version import (
            get_offer_versions as external_get_offer_versions,
        )

        return external_get_offer_versions(*offer_version_ids)
    else:
        raise Exception(f"Unknown offer category: {offer_category}")

paginate_offer_versions

paginate_offer_versions(
    filters,
    eligible_for_company_id,
    eligible_for_ccn_code,
    corresponding_builder_product_id=None,
    page=1,
    per_page=1000,
)
Source code in components/offer_catalog/public/offer_version.py
def paginate_offer_versions(  # noqa: D103
    filters: Filters[OfferVersionCriterionName],
    eligible_for_company_id: int | None,
    eligible_for_ccn_code: str | None,
    corresponding_builder_product_id: str | None = None,
    page: int = 1,
    per_page: int = 1000,
) -> FilterResult[OfferVersionCriterionName, SearcheableOfferVersionItem]:
    from components.offer_catalog.external.eligibility import (
        get_eligible_offer_versions,
    )
    from components.offer_catalog.external.health_offer_version import (
        OfferVersionType,
        get_all_offer_versions,
        get_offer_versions_for_builder_product,
    )

    # NOTE: This might be a bit expensive in terms of memory. Let's see.
    all_offer_versions = get_all_offer_versions(
        plan_types=(OfferVersionType.tailored, OfferVersionType.semi_tailored)
        if corresponding_builder_product_id
        else (
            OfferVersionType.standard,
            OfferVersionType.national,
            OfferVersionType.individual,
        )
    )

    if eligible_for_company_id or eligible_for_ccn_code:
        eligible_bundle_versions = get_eligible_offer_versions(
            company_id=eligible_for_company_id,
            ccn_code=eligible_for_ccn_code,
        )
    else:
        eligible_bundle_versions = None

    if corresponding_builder_product_id is not None:
        product_builder_bundle_versions = {
            v.bundle_version
            for v in get_offer_versions_for_builder_product(
                builder_product_id=corresponding_builder_product_id
            )
        }
    else:
        product_builder_bundle_versions = None

    filter_criteria = filters.search_criteria
    filtered_offer_versions = [
        offer_version
        for offer_version in all_offer_versions
        if (
            eligible_bundle_versions is None
            or offer_version.bundle_version in eligible_bundle_versions
        )
        and (
            product_builder_bundle_versions is None
            or offer_version.bundle_version in product_builder_bundle_versions
        )
        and offer_version.get_search_criteria().matches(filter_criteria)
    ]

    return FilterResult.from_full_list(  # type: ignore[no-any-return]
        items=filtered_offer_versions,
        page=page,
        per_page=per_page,
        filters=filters,
        all_available_items=list(all_offer_versions),
    )

search_offer_versions

search_offer_versions(search_term)
Source code in components/offer_catalog/public/offer_version.py
def search_offer_versions(  # noqa: D103
    search_term: str,
) -> list[OfferVersion]:
    from components.offer_catalog.external.health_offer_version import (
        search_offer_versions as external_search_offer_versions,
    )

    return external_search_offer_versions(search_term=search_term)

components.offer_catalog.public.prevoyance_offer_version

paginate_prevoyance_offer_versions

paginate_prevoyance_offer_versions(
    filters, page=1, per_page=1000
)
Source code in components/offer_catalog/public/prevoyance_offer_version.py
def paginate_prevoyance_offer_versions(  # noqa: D103
    filters: Filters[PrevoyanceOfferVersionCriterionName],
    page: int = 1,
    per_page: int = 1000,
) -> FilterResult[
    PrevoyanceOfferVersionCriterionName,
    SearcheablePrevoyanceOfferVersionItem,
]:
    from components.offer_catalog.external.prevoyance_offer_version import (
        get_all_prevoyance_offer_versions,
    )

    # NOTE: This might be a bit expensive in terms of memory. Let's see.
    all_prevoyance_offer_versions = get_all_prevoyance_offer_versions()

    filter_criteria = filters.search_criteria
    filtered_prevoyance_offer_versions = [
        prevoyance_offer_version
        for prevoyance_offer_version in all_prevoyance_offer_versions
        if prevoyance_offer_version.get_search_criteria().matches(filter_criteria)
    ]

    return FilterResult.from_full_list(  # type: ignore[no-any-return]
        items=filtered_prevoyance_offer_versions,
        page=page,
        per_page=per_page,
        filters=filters,
        all_available_items=list(all_prevoyance_offer_versions),
    )