Skip to content

Api reference

components.global_account.public.account_entity

AccountStatus

Bases: AlanBaseEnum

churned class-attribute instance-attribute

churned = 'churned'

live class-attribute instance-attribute

live = 'live'

prospect class-attribute instance-attribute

prospect = 'prospect'

delete_account_if_it_has_no_entity

delete_account_if_it_has_no_entity(
    account_id, new_account_id
)
Source code in components/global_account/external/account.py
@tracer.wrap(service="global_account")
def delete_account_if_it_has_no_entity(account_id: UUID, new_account_id: UUID) -> None:
    from components.fr.internal.business_logic.account.actions.account import (  # noqa: ALN043
        delete_account,
        move_account_related_models_to_new_account,
    )

    account_sqla = get_or_raise_missing_resource(AccountSQLA, account_id)
    if len(account_sqla.companies) == 0:
        # Move related data to the new account
        move_account_related_models_to_new_account(new_account_id, account_id)

    account = get_account(account_id)
    if len(account.entities) == 0:
        current_logger.info(
            f"Deleting {account} because it has no entity left", account_id=account_id
        )
        delete_account(account_id)

get_account

get_account(account_id)

Return full account, delegating to the app's account repository.

Source code in components/global_account/external/account.py
@tracer.wrap(service="global_account")
def get_account(account_id: UUID) -> Account:
    """Return full account, delegating to the app's account repository."""
    return get_account_repository().get_full_account(account_id)

get_account_entities

get_account_entities(account_id)

Return entities for an account, delegating to the app's account repository.

Source code in components/global_account/external/account.py
@tracer.wrap(service="global_account")
def get_account_entities(account_id: UUID) -> list[Entity]:
    """Return entities for an account, delegating to the app's account repository."""
    return get_account_repository().get_full_account(account_id).entities

get_light_account

get_light_account(account_ref)
Source code in components/global_account/external/account.py
def get_light_account(account_ref: str) -> LightAccount:
    account = get_or_raise_missing_resource(AccountSQLA, account_ref)
    return get_light_account_from_model(account)

components.global_account.public.account_repository

get_account_repository

get_account_repository()

Return the AccountRepository for the current app's country.

Source code in components/global_account/public/account_repository.py
def get_account_repository() -> AccountRepository:
    """Return the AccountRepository for the current app's country."""
    return get_app_dependency().get_account_repository()

components.global_account.public.actions

bookmark_account

bookmark_account

bookmark_account(account_id, user_id)

Bookmark an account for a specific user. Only creates the bookmark if it doesn't already exist.

Source code in components/global_account/public/actions/bookmark_account.py
def bookmark_account(account_id: UUID, user_id: str) -> None:
    """
    Bookmark an account for a specific user.
    Only creates the bookmark if it doesn't already exist.
    """
    existing_bookmark = (
        current_session.query(BookmarkAccount)  # noqa: ALN085
        .filter(
            BookmarkAccount.account_id == account_id,
            BookmarkAccount.user_id == user_id,
        )
        .first()
    )
    if not existing_bookmark:
        bookmark = BookmarkAccount(account_id=account_id, user_id=user_id)
        current_session.add(bookmark)
        current_logger.info(
            f"Bookmark created for user {user_id} and account {account_id}"
        )
    else:
        current_logger.info(
            f"Bookmark already exists for user {user_id} and account {account_id}"
        )

unbookmark_account

unbookmark_account(account_id, user_id)

Remove bookmark for an account for a specific user.

Source code in components/global_account/public/actions/bookmark_account.py
def unbookmark_account(account_id: UUID, user_id: str) -> None:
    """
    Remove bookmark for an account for a specific user.
    """
    bookmarks = (
        current_session.query(BookmarkAccount)  # noqa: ALN085
        .filter(
            BookmarkAccount.account_id == account_id,
            BookmarkAccount.user_id == user_id,
        )
        .all()
    )
    if bookmarks:
        for bookmark in bookmarks:
            current_session.delete(bookmark)

        current_logger.info(
            f"Bookmark removed for user {user_id} and account {account_id}"
        )

builder_prospect

get_or_create_prospect_from_company module-attribute

get_or_create_prospect_from_company = (
    get_or_create_prospect_from_company
)

update_builder_prospect module-attribute

update_builder_prospect = update_builder_prospect

components.global_account.public.builder_prospect

get_builder_prospect_by_ccn_and_siren

get_builder_prospect_by_ccn_and_siren(
    ccn_code, siren, nic=None, is_from_self_serve=None
)

Return the CompanyProspect for the given ccn_code and SIREN + NIC if given, or None if not found.

Source code in components/global_account/public/builder_prospect.py
def get_builder_prospect_by_ccn_and_siren(
    ccn_code: str,
    siren: str,
    nic: str | None = None,
    is_from_self_serve: bool | None = None,
) -> CompanyProspect | None:
    """
    Return the CompanyProspect for the given ccn_code and SIREN + NIC if given, or None if not found.
    """
    from components.global_account.internal.models.builder_prospect import (
        BuilderProspect,
    )

    stmt = select(BuilderProspect).where(
        BuilderProspect.company_identifier == siren,
        BuilderProspect.industry_framework_agreement_code == ccn_code,
        BuilderProspect.specific_company_identifier == nic,
    )

    if is_from_self_serve is not None:
        stmt = stmt.where(BuilderProspect.is_from_self_serve == is_from_self_serve)

    builder_prospect_model = current_session.scalars(
        stmt.order_by(
            # Adding order_by to make the query deterministic (even though we should only get 1 result)
            BuilderProspect.created_at.desc()
        )
    ).first()

    return (
        get_company_prospect_or_none(builder_prospect_model.id)
        if builder_prospect_model
        else None
    )

get_company_prospect

get_company_prospect(id)
Source code in components/global_account/internal/queries/builder_prospect.py
def get_company_prospect(id: UUID | str) -> CompanyProspect:
    return one(get_company_prospects([id]))

get_company_prospect_or_none

get_company_prospect_or_none(id)
Source code in components/global_account/internal/queries/builder_prospect.py
def get_company_prospect_or_none(id: UUID | str) -> CompanyProspect | None:
    return one_or_none(get_company_prospects([id], raise_if_some_missing=False))

get_company_prospects

get_company_prospects(ids, raise_if_some_missing=True)
Source code in components/global_account/internal/queries/builder_prospect.py
def get_company_prospects(
    ids: Sequence[UUID | str], raise_if_some_missing: bool = True
) -> list[CompanyProspect]:
    if not ids:
        return []

    builder_prospects = get_account_repository().search_builder_prospects(
        prospect_refs=[str(id_) for id_ in ids]
    )

    found_ids = {str(bp.id) for bp in builder_prospects}
    missing_ids = {str(id_) for id_ in ids} - found_ids
    if missing_ids and raise_if_some_missing:
        raise BaseErrorCode.missing_resource(
            message=f"BuilderProspects ids={missing_ids} not found"
        )
    return [CompanyProspect.from_builder_prospect(bp) for bp in builder_prospects]

get_or_create_builder_prospects_from_companies

get_or_create_builder_prospects_from_companies(company_ids)

Return a mapping (company_ref -> prospect) for all the provided companies. If the company already has a builder prospect, the existing builder prospect is returned. Otherwise, a new builder prospect is created.

Source code in components/global_account/internal/actions/builder_prospect.py
def get_or_create_builder_prospects_from_companies(
    company_ids: list[int],
) -> dict[str, CompanyProspect]:
    """
    Return a mapping (company_ref -> prospect) for all the provided companies.
    If the company already has a builder prospect, the existing builder prospect
    is returned. Otherwise, a new builder prospect is created.
    """
    from components.fr.internal.models.company import (  # noqa: ALN069
        Company,
    )

    if not company_ids:
        return {}

    companies: list[Company] = (
        current_session.query(Company)  # noqa: ALN085
        .options(
            joinedload(Company.address),
            joinedload(Company.ccn),
            joinedload(Company.franchise),
        )
        .filter(Company.id.in_(company_ids))
        .all()
    )
    account_id = one(
        set(company.account_id for company in companies),
        "All companies must belong to the same account",
    )

    prospects: list[BuilderProspectSQLA] = (
        current_session.query(BuilderProspectSQLA)  # noqa: ALN085
        .filter(
            BuilderProspectSQLA.alan_account_id == str(account_id),
            BuilderProspectSQLA.company_ref.in_(
                str(company.id) for company in companies
            ),
        )
        .all()
    )

    companies_with_prospects = {prospect.company_ref for prospect in prospects}

    for company in companies:
        if str(company.id) not in companies_with_prospects:
            prospects.append(_create_builder_prospect_from_company(company))

    return {
        mandatory(company_prospect.company_ref): company_prospect
        for company_prospect in get_company_prospects(
            [str(prospect.id) for prospect in prospects]
        )
    }

get_or_create_prospects_from_account_id_and_entities

get_or_create_prospects_from_account_id_and_entities(
    account_id, entities
)

Fetches all the builder prospects from the account (possibly creates prospects if only comnpanies exist) then returns all the prospects that are referenced by the entities list

Source code in components/global_account/internal/actions/builder_prospect.py
def get_or_create_prospects_from_account_id_and_entities(
    account_id: uuid.UUID,
    entities: list[EntityRef],
) -> list[CompanyProspect]:
    """Fetches all the builder prospects from the account (possibly creates prospects if only comnpanies exist)
    then returns all the prospects that are referenced by the entities list"""
    builder_prospects = get_account_repository().get_or_create_builder_prospects(
        account_id
    )

    relevant_prospects = [
        bp
        for bp in builder_prospects
        for entity in entities
        if str(bp.alan_account_id) == str(account_id)
        and (
            str(bp.id) == str(entity.prospect_ref)
            or (
                bp.company is not None
                and entity.company_ref is not None
                and str(bp.company.id) == str(entity.company_ref)
            )
        )
    ]

    if len(entities) != len(relevant_prospects):
        matched_prospect_ids = {str(bp.id) for bp in relevant_prospects}
        matched_company_ids = {
            str(bp.company.id) for bp in relevant_prospects if bp.company is not None
        }
        unmatched = [
            entity
            for entity in entities
            if (
                entity.prospect_ref is None
                or str(entity.prospect_ref) not in matched_prospect_ids
            )
            and (
                entity.company_ref is None
                or str(entity.company_ref) not in matched_company_ids
            )
        ]
        raise BaseErrorCode.invalid_arguments(
            description=(
                f"Could not resolve prospects on account {account_id} for entities: "
                + ", ".join(
                    f"(prospect_ref={e.prospect_ref}, company_ref={e.company_ref})"
                    for e in unmatched
                )
                + ". Please open an Eng on-call card with these IDs — the Company may live on a different account."
            )
        )

    return [CompanyProspect.from_builder_prospect(bp) for bp in relevant_prospects]

get_or_create_prospects_from_entities

get_or_create_prospects_from_entities(entities)

Get or create builder prospects from entities.

  • If the entity has a prospect_ref, get the prospect by ID from the DB.
  • If the entity has a company_ref:
  • get the builder prospect for the company if it exists.
  • else create it.
Source code in components/global_account/internal/actions/builder_prospect.py
def get_or_create_prospects_from_entities(
    entities: list[EntityRef],
) -> list[CompanyProspect]:
    """
    Get or create builder prospects from entities.

    - If the entity has a prospect_ref, get the prospect by ID from the DB.
    - If the entity has a company_ref:
      - get the builder prospect for the company if it exists.
      - else create it.
    """
    company_prospects: list[CompanyProspect] = []
    with_prospect, without_prospect = partition(
        entities, lambda e: e.prospect_ref is not None
    )

    company_prospects += get_company_prospects(
        [mandatory(e.prospect_ref) for e in with_prospect]
    )

    company_prospects += get_or_create_builder_prospects_from_companies(
        [int(mandatory(e.company_ref)) for e in without_prospect]
    ).values()

    return company_prospects

get_self_serve_builder_prospect

get_self_serve_builder_prospect(onboarding_id, country)

Return the most recent self-serve BuilderProspect for the given onboarding_id and country.

Source code in components/global_account/public/builder_prospect.py
def get_self_serve_builder_prospect(
    onboarding_id: str,
    country: GlobalAccountSupportedCountry,
) -> BuilderProspect | None:
    """Return the most recent self-serve BuilderProspect for the given onboarding_id and country."""
    from components.global_account.internal.mappers.builder_prospect_mapper import (
        BuilderProspectMapper,
    )
    from components.global_account.internal.models.builder_prospect import (
        BuilderProspect as BuilderProspectSQLA,
    )
    from components.global_account.internal.repositories.account_repository import (
        create_account_repository,
    )

    model = current_session.scalars(
        select(BuilderProspectSQLA)
        .where(
            BuilderProspectSQLA.onboarding_id == onboarding_id,
            BuilderProspectSQLA.is_from_self_serve.is_(True),
            BuilderProspectSQLA.country == country,
        )
        .order_by(BuilderProspectSQLA.created_at.desc())
    ).first()
    if not model:
        return None
    account_repository = create_account_repository(country=country)
    company = (
        account_repository.get_company(model.company_ref) if model.company_ref else None
    )
    return BuilderProspectMapper.to_entity(model, company)

components.global_account.public.country_specific

be

dependencies

global_account_dependency module-attribute
global_account_dependency = BeGlobalAccountDependency()

es

dependencies

global_account_dependency module-attribute
global_account_dependency = EsGlobalAccountDependency()

fr

dependencies

global_account_dependency module-attribute
global_account_dependency = FrGlobalAccountDependency()

components.global_account.public.dependencies

COMPONENT_NAME module-attribute

COMPONENT_NAME = 'global_account'

GlobalAccountDependency

Bases: ABC

Abstract class defining the dependencies needed by the Global Account component.

get_account_repository

get_account_repository()

Returns the AccountRepository for this app's country.

Source code in components/global_account/public/dependencies.py
def get_account_repository(self) -> AccountRepository:
    """Returns the AccountRepository for this app's country."""
    from components.global_account.internal.repositories.account_repository import (
        create_account_repository,
    )

    return create_account_repository(self.get_country_code())

get_country_code abstractmethod

get_country_code()

Returns the country code for this app.

Source code in components/global_account/public/dependencies.py
@abstractmethod
def get_country_code(self) -> GlobalAccountSupportedCountry:
    """Returns the country code for this app."""

get_franchises_refs_by_name

get_franchises_refs_by_name()

Returns a mapping of franchise name → franchise ref (UUID string).

Override in country-specific dependencies where franchises exist (e.g. FR).

Source code in components/global_account/public/dependencies.py
def get_franchises_refs_by_name(self) -> dict[str, str]:
    """Returns a mapping of franchise name → franchise ref (UUID string).

    Override in country-specific dependencies where franchises exist (e.g. FR).
    """
    return {}

get_app_dependency

get_app_dependency()

Retrieve the Global Account dependency from the current app.

Source code in components/global_account/public/dependencies.py
def get_app_dependency() -> GlobalAccountDependency:
    """Retrieve the Global Account dependency from the current app."""
    from flask import current_app

    return cast(
        "GlobalAccountDependency",
        cast("CustomFlask", current_app).get_component_dependency(COMPONENT_NAME),
    )

set_app_dependency

set_app_dependency(dependency)

Set the Global Account dependency of the current app.

Source code in components/global_account/public/dependencies.py
def set_app_dependency(dependency: GlobalAccountDependency) -> None:
    """Set the Global Account dependency of the current app."""
    from flask import current_app

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

components.global_account.public.entities

account

Account dataclass

Account(*, id, name, country)

Bases: DataClassJsonMixin

Represents an account.

country instance-attribute
country
id instance-attribute
id
name instance-attribute
name

AccountManagementLevel

Bases: AlanBaseEnum

bronze class-attribute instance-attribute
bronze = 'bronze'
gold class-attribute instance-attribute
gold = 'gold'
gold_network class-attribute instance-attribute
gold_network = 'gold_network'
mass class-attribute instance-attribute
mass = 'mass'
mass_ka class-attribute instance-attribute
mass_ka = 'mass_ka'
platinum class-attribute instance-attribute
platinum = 'platinum'
public_sector class-attribute instance-attribute
public_sector = 'public_sector'
regular class-attribute instance-attribute
regular = 'regular'
silver class-attribute instance-attribute
silver = 'silver'
silver_network class-attribute instance-attribute
silver_network = 'silver_network'

AccountStatus

Bases: AlanBaseEnum

churned class-attribute instance-attribute
churned = 'churned'
live class-attribute instance-attribute
live = 'live'
prospect class-attribute instance-attribute
prospect = 'prospect'

builder_prospect_api

BuilderProspectAPISchema dataclass

BuilderProspectAPISchema(
    name,
    postal_code,
    country,
    siren=None,
    onboarding_id=None,
    ccn_code=None,
    ape_code=None,
    company_creation_year=None,
    number_of_primaries=None,
    franchise_name=None,
    alan_account_id=None,
    salesforce_account_id=None,
    salesforce_opportunity_id=None,
    nic=None,
    is_from_self_serve=False,
    company_ref=None,
    is_virtual=False,
    source_prospect_ids=list(),
)

Bases: DataClassJsonMixin

__post_init__
__post_init__()

Validate that at least one identifier is set (unless virtual).

Source code in components/global_account/public/entities/builder_prospect_api.py
def __post_init__(self) -> None:
    """Validate that at least one identifier is set (unless virtual)."""
    if not self.is_virtual and not self.siren and not self.onboarding_id:
        raise ValueError("At least one of siren or onboarding_id must be set")
alan_account_id class-attribute instance-attribute
alan_account_id = None
ape_code class-attribute instance-attribute
ape_code = None
ccn_code class-attribute instance-attribute
ccn_code = None
company_creation_year class-attribute instance-attribute
company_creation_year = None
company_ref class-attribute instance-attribute
company_ref = None
country instance-attribute
country
franchise_name class-attribute instance-attribute
franchise_name = None
is_from_self_serve class-attribute instance-attribute
is_from_self_serve = False
is_virtual class-attribute instance-attribute
is_virtual = False
name instance-attribute
name
nic class-attribute instance-attribute
nic = None
number_of_primaries class-attribute instance-attribute
number_of_primaries = None
onboarding_id class-attribute instance-attribute
onboarding_id = None
postal_code instance-attribute
postal_code
salesforce_account_id class-attribute instance-attribute
salesforce_account_id = None
salesforce_opportunity_id class-attribute instance-attribute
salesforce_opportunity_id = None
siren class-attribute instance-attribute
siren = None
source_prospect_ids class-attribute instance-attribute
source_prospect_ids = field(default_factory=list)

PatchBuilderProspectAPISchema dataclass

PatchBuilderProspectAPISchema(
    siren=NOT_SET,
    onboarding_id=NOT_SET,
    name=NOT_SET,
    ccn_code=NOT_SET,
    ape_code=NOT_SET,
    postal_code=NOT_SET,
    company_creation_year=NOT_SET,
    number_of_primaries=NOT_SET,
    franchise_name=NOT_SET,
    alan_account_id=NOT_SET,
    salesforce_account_id=NOT_SET,
    salesforce_opportunity_id=NOT_SET,
    nic=NOT_SET,
    is_from_self_serve=NOT_SET,
    company_ref=NOT_SET,
    source_prospect_ids=NOT_SET,
)

Bases: DataClassJsonMixin

alan_account_id class-attribute instance-attribute
alan_account_id = NOT_SET
ape_code class-attribute instance-attribute
ape_code = NOT_SET
ccn_code class-attribute instance-attribute
ccn_code = NOT_SET
company_creation_year class-attribute instance-attribute
company_creation_year = NOT_SET
company_ref class-attribute instance-attribute
company_ref = NOT_SET
franchise_name class-attribute instance-attribute
franchise_name = NOT_SET
is_from_self_serve class-attribute instance-attribute
is_from_self_serve = NOT_SET
name class-attribute instance-attribute
name = NOT_SET
nic class-attribute instance-attribute
nic = NOT_SET
number_of_primaries class-attribute instance-attribute
number_of_primaries = NOT_SET
onboarding_id class-attribute instance-attribute
onboarding_id = NOT_SET
postal_code class-attribute instance-attribute
postal_code = NOT_SET
salesforce_account_id class-attribute instance-attribute
salesforce_account_id = NOT_SET
salesforce_opportunity_id class-attribute instance-attribute
salesforce_opportunity_id = NOT_SET
siren class-attribute instance-attribute
siren = NOT_SET
source_prospect_ids class-attribute instance-attribute
source_prospect_ids = NOT_SET

company_prospect

CompanyProspect dataclass

CompanyProspect(
    *,
    alan_account_id,
    salesforce_account_id=None,
    salesforce_opportunity_id=None,
    name,
    formatted_name,
    siren,
    nic=None,
    ape_code,
    ccn_code,
    franchise_name,
    franchise_ref,
    postal_code,
    company_creation_year,
    number_of_primaries,
    is_from_self_serve,
    company_ref=None,
    is_virtual=False,
    source_prospect_ids=list(),
    id,
    country
)

Bases: BaseEntity, DataClassJsonMixin

company_ref class-attribute instance-attribute
company_ref = None
country instance-attribute
country
from_builder_prospect classmethod
from_builder_prospect(bp)
Source code in components/global_account/internal/entities/company_prospect.py
@classmethod
def from_builder_prospect(
    cls,
    bp: BuilderProspect,
) -> CompanyProspect:
    return cls(
        alan_account_id=str(bp.alan_account_id)
        if bp.alan_account_id is not None
        else None,
        salesforce_account_id=bp.company.salesforce_account_id
        if bp.company is not None
        else bp.salesforce_account_id,
        salesforce_opportunity_id=None
        if bp.company is not None
        else bp.salesforce_opportunity_id,
        name=bp.company.name if bp.company is not None else bp.name,
        formatted_name=(bp.company.name if bp.company is not None else bp.name)
        .lower()
        .title(),
        siren=bp.company.company_identifier
        if bp.company is not None
        else bp.company_identifier,
        nic=bp.company.specific_company_identifier
        if bp.company is not None
        else bp.specific_company_identifier,
        ape_code=bp.company.main_business_activity_code
        if bp.company is not None
        else bp.main_business_activity_code,
        ccn_code=bp.company.industry_framework_agreement_code
        if bp.company is not None
        else bp.industry_framework_agreement_code,
        franchise_name=bp.company.franchise_name
        if bp.company is not None
        else bp.franchise_name,
        franchise_ref=bp.company.franchise_ref
        if bp.company is not None
        else bp.franchise_ref,
        postal_code=bp.company.postal_code
        if bp.company is not None
        else bp.postal_code,
        # 2020 not great as default but mirrors legacy fallback
        company_creation_year=bp.creation_year or 2020,
        number_of_primaries=bp.number_of_primaries,
        is_from_self_serve=bp.is_from_self_serve,
        company_ref=bp.company.id if bp.company is not None else None,
        id=bp.id,
        country=bp.country,
        is_virtual=bp.is_virtual,
        source_prospect_ids=list(bp.source_prospect_ids),
    )
id instance-attribute
id

EntityRef dataclass

EntityRef(company_ref=None, prospect_ref=None)

Bases: DataClassJsonMixin, DataClassJsonAlanMixin

__post_init__
__post_init__()
Source code in components/global_account/internal/entities/entity.py
def __post_init__(self):  # type: ignore[no-untyped-def]
    if self.company_ref is None and self.prospect_ref is None:
        raise ValueError("At least one of company_ref or prospect_ref must be set")
company_ref class-attribute instance-attribute
company_ref = None
prospect_ref class-attribute instance-attribute
prospect_ref = None

global_builder_prospects

Address dataclass

Address(*, street, city)

Bases: DataClassJsonMixin

Represents a company address

city instance-attribute
city
street instance-attribute
street

BuilderProspect dataclass

BuilderProspect(
    *,
    id,
    alan_account_id,
    country,
    name,
    salesforce_account_id=None,
    industry_framework_agreement_code=None,
    main_business_activity_code=None,
    company_identifier=None,
    onboarding_id=None,
    specific_company_identifier=None,
    franchise_name=None,
    franchise_ref=None,
    postal_code=None,
    salesforce_opportunity_id=None,
    is_from_self_serve,
    creation_year=None,
    number_of_primaries=None,
    company,
    is_virtual=False,
    source_prospect_ids=()
)

Bases: DataClassJsonMixin

Either points to a real company (company is not None) or represents a potential company.

alan_account_id instance-attribute
alan_account_id
company instance-attribute
company
company_identifier class-attribute instance-attribute
company_identifier = None
country instance-attribute
country
creation_year class-attribute instance-attribute
creation_year = None
franchise_name class-attribute instance-attribute
franchise_name = None
franchise_ref class-attribute instance-attribute
franchise_ref = None
id instance-attribute
id
industry_framework_agreement_code class-attribute instance-attribute
industry_framework_agreement_code = None
is_from_self_serve instance-attribute
is_from_self_serve
is_virtual class-attribute instance-attribute
is_virtual = False
main_business_activity_code class-attribute instance-attribute
main_business_activity_code = None
name instance-attribute
name
number_of_primaries class-attribute instance-attribute
number_of_primaries = None
onboarding_id class-attribute instance-attribute
onboarding_id = None
postal_code class-attribute instance-attribute
postal_code = None
salesforce_account_id class-attribute instance-attribute
salesforce_account_id = None
salesforce_opportunity_id class-attribute instance-attribute
salesforce_opportunity_id = None
source_prospect_ids class-attribute instance-attribute
source_prospect_ids = ()
specific_company_identifier class-attribute instance-attribute
specific_company_identifier = None

Company dataclass

Company(
    *,
    id,
    alan_account_id,
    country,
    name,
    salesforce_account_id=None,
    franchise_name=None,
    franchise_ref=None,
    company_identifier,
    specific_company_identifier=None,
    main_business_activity_code=None,
    industry_framework_agreement_code=None,
    postal_code=None,
    address=None,
    number_of_primaries=None
)

Bases: DataClassJsonMixin

Represents an actual Company

address class-attribute instance-attribute
address = None
alan_account_id instance-attribute
alan_account_id
company_identifier instance-attribute
company_identifier
country instance-attribute
country
franchise_name class-attribute instance-attribute
franchise_name = None
franchise_ref class-attribute instance-attribute
franchise_ref = None
id instance-attribute
id
industry_framework_agreement_code class-attribute instance-attribute
industry_framework_agreement_code = None
main_business_activity_code class-attribute instance-attribute
main_business_activity_code = None
name instance-attribute
name
number_of_primaries class-attribute instance-attribute
number_of_primaries = None
postal_code class-attribute instance-attribute
postal_code = None
salesforce_account_id class-attribute instance-attribute
salesforce_account_id = None
specific_company_identifier class-attribute instance-attribute
specific_company_identifier = None

proposal

FROM_OFFER_BUILDER_TAG module-attribute

FROM_OFFER_BUILDER_TAG = 'from_offer_builder'

proposal_api

ProposalItemAPISchema dataclass

ProposalItemAPISchema(
    builder_product_id=None,
    prevoyance_builder_product_id=None,
    plan_id=None,
    company_ids=list(),
)

Bases: DataClassJsonMixin, DataClassJsonAlanMixin

__post_init__
__post_init__()
Source code in components/global_account/public/entities/proposal_api.py
def __post_init__(self):  # type: ignore[no-untyped-def]  # noqa: D105
    if (
        (self.builder_product_id is not None)
        + (self.plan_id is not None)
        + (self.prevoyance_builder_product_id is not None)
    ) != 1:
        raise ValueError(
            "Exactly one of builder_product_id or prevoyance_builder_product_id or plan_id must be provided"
        )
    # Make sure ints are indeed ints
    if self.builder_product_id is not None:
        self.builder_product_id = int(self.builder_product_id)
    if self.plan_id is not None:
        self.plan_id = int(self.plan_id)
    self.company_ids = [str(company_id) for company_id in self.company_ids]
builder_product_id class-attribute instance-attribute
builder_product_id = None
company_ids class-attribute instance-attribute
company_ids = field(default_factory=list)
plan_id class-attribute instance-attribute
plan_id = None
prevoyance_builder_product_id class-attribute instance-attribute
prevoyance_builder_product_id = None

components.global_account.public.enum

global_account_supported_country

GlobalAccountSupportedCountry

Bases: AlanBaseEnum

Enum representing possible countries supported by global account.

be class-attribute instance-attribute
be = 'be'
es class-attribute instance-attribute
es = 'es'
fr class-attribute instance-attribute
fr = 'fr'
from_app_name classmethod
from_app_name(app_name)

Convert an app name to the corresponding global account country.

Source code in components/global_account/public/enum/global_account_supported_country.py
@classmethod
def from_app_name(cls, app_name: AppName) -> "GlobalAccountSupportedCountry":
    """
    Convert an app name to the corresponding global account country.
    """
    if app_name not in _APP_NAME_TO_COUNTRY:
        raise NotImplementedError(
            "Translation from app name to global account country is not implemented for this app name."
        )
    return _APP_NAME_TO_COUNTRY[app_name]
to_app_name
to_app_name()

Convert the country code to the corresponding app name.

Source code in components/global_account/public/enum/global_account_supported_country.py
def to_app_name(self) -> AppName:
    """
    Convert the country code to the corresponding app name.
    """
    if self not in _COUNTRY_TO_APP_NAME:
        raise NotImplementedError(
            "Translation from global account country to app name is not implemented for this country."
        )
    return _COUNTRY_TO_APP_NAME[self]

mapper

global_account_supported_country

GlobalAccountSupportedCountryMapper

Mapper between GlobalAccountSupportedCountry and AppName

from_salesforce_account staticmethod
from_salesforce_account(account)

Gets the Country from an SalesforceAccount

Source code in components/global_account/public/enum/mapper/global_account_supported_country.py
@staticmethod
def from_salesforce_account(
    account: SalesforceAccount,
) -> GlobalAccountSupportedCountry:
    """Gets the Country from an SalesforceAccount"""
    if account.country is None:
        current_logger.warning(
            f"Missing country for SalesforceAccount ID {account.id}"
        )
        return GlobalAccountSupportedCountry.fr

    clean_country = account.country.lower().title()

    if not SalesforceAccountCountry.has_value(clean_country):
        current_logger.warning(
            f"Unknown country {clean_country} for SalesforceAccount ID {account.id}"
        )
        return GlobalAccountSupportedCountry.fr

    country = SalesforceAccountCountry.validate(clean_country)

    match country:
        case SalesforceAccountCountry.fr:
            return GlobalAccountSupportedCountry.fr
        case SalesforceAccountCountry.be:
            return GlobalAccountSupportedCountry.be
        case SalesforceAccountCountry.es:
            return GlobalAccountSupportedCountry.es
        case _:
            raise ValueError(f"Unknown country {account.country}")
to_app_name staticmethod
to_app_name(country)

Maps a GlobalAccountSupportedCountry to an AppName

Source code in components/global_account/public/enum/mapper/global_account_supported_country.py
@staticmethod
def to_app_name(country: GlobalAccountSupportedCountry) -> AppName:
    """Maps a GlobalAccountSupportedCountry to an AppName"""
    match country:
        case GlobalAccountSupportedCountry.fr:
            return AppName.ALAN_FR
        case GlobalAccountSupportedCountry.be:
            return AppName.ALAN_BE
        case GlobalAccountSupportedCountry.es:
            return AppName.ALAN_ES
        case _:
            raise ValueError(f"Unknown country {country}")

components.global_account.public.events

ProposalCreated dataclass

ProposalCreated(
    *,
    proposal_id,
    health_builder_product_ids,
    prevoyance_builder_product_ids,
    country
)

Bases: WebhookMessage

The event is triggered when a proposal is created from within offer builder.

country instance-attribute

country

health_builder_product_ids instance-attribute

health_builder_product_ids

prevoyance_builder_product_ids instance-attribute

prevoyance_builder_product_ids

proposal_id instance-attribute

proposal_id

components.global_account.public.queries

search_accounts

SearchAccount dataclass

SearchAccount(
    id,
    app_name,
    name=None,
    min_start_date=None,
    max_end_date=None,
    is_key_account=None,
    am_level=None,
    is_unmanaged_key_account=None,
    owner_name=None,
    value_segment=None,
    sub_industry=None,
    franchise_name=None,
    employee_count=None,
    signed_total_arr=None,
    loss_ratio_credibility=None,
    credibility=None,
    status=None,
    live_prevoyance_contract_count=None,
    live_health_contract_count=None,
    company_count=None,
    has_health_contract=None,
    has_prevoyance_contract=None,
    bookmarked_by_user_ids=list(),
    n_live_primaries=None,
)

Bases: DataClassJsonMixin

This entity is used to expose the search accounts. Aggregated data computed by Turing and refreshed by Airflow using export backend DAG

am_level class-attribute instance-attribute
am_level = None
app_name instance-attribute
app_name
bookmarked_by_user_ids class-attribute instance-attribute
bookmarked_by_user_ids = field(default_factory=list)
company_count class-attribute instance-attribute
company_count = None
credibility class-attribute instance-attribute
credibility = None
employee_count class-attribute instance-attribute
employee_count = None
franchise_name class-attribute instance-attribute
franchise_name = None
from_model classmethod
from_model(model)

Convert a SQLAlchemy model instance to a SearchAccount instance.

Source code in components/global_account/public/queries/search_accounts.py
@classmethod
def from_model(cls, model: TuringSearchAccountSQLA) -> "SearchAccount":
    """
    Convert a SQLAlchemy model instance to a SearchAccount instance.
    """
    return cls(
        id=model.id,
        name=model.name,
        min_start_date=model.min_start_date,
        max_end_date=model.max_end_date,
        is_key_account=model.is_key_account,
        am_level=model.am_level,
        is_unmanaged_key_account=model.is_unmanaged_key_account,
        owner_name=model.owner_name,
        value_segment=model.value_segment,
        sub_industry=model.sub_industry,
        franchise_name=model.franchise_name,
        employee_count=model.employee_count,
        signed_total_arr=model.signed_total_arr,
        loss_ratio_credibility=model.loss_ratio_credibility,
        status=AccountStatus(model.status) if model.status else None,
        live_prevoyance_contract_count=model.live_prevoyance_contract_count,
        live_health_contract_count=model.live_health_contract_count,
        company_count=model.company_count,
        has_health_contract=(
            model.live_health_contract_count is not None
            and model.live_health_contract_count > 0
        ),
        has_prevoyance_contract=(
            model.live_prevoyance_contract_count is not None
            and model.live_prevoyance_contract_count > 0
        ),
        bookmarked_by_user_ids=model.bookmarked_by_user_ids,
        n_live_primaries=model.n_live_primaries,
        credibility=model.credibility,
        app_name=AppName(model.app_name),
    )
has_health_contract class-attribute instance-attribute
has_health_contract = None
has_prevoyance_contract class-attribute instance-attribute
has_prevoyance_contract = None
id instance-attribute
id
is_key_account class-attribute instance-attribute
is_key_account = None
is_unmanaged_key_account class-attribute instance-attribute
is_unmanaged_key_account = None
live_health_contract_count class-attribute instance-attribute
live_health_contract_count = None
live_prevoyance_contract_count class-attribute instance-attribute
live_prevoyance_contract_count = None
loss_ratio_credibility class-attribute instance-attribute
loss_ratio_credibility = None
max_end_date class-attribute instance-attribute
max_end_date = None
min_start_date class-attribute instance-attribute
min_start_date = None
n_live_primaries class-attribute instance-attribute
n_live_primaries = None
name class-attribute instance-attribute
name = None
owner_name class-attribute instance-attribute
owner_name = None
signed_total_arr class-attribute instance-attribute
signed_total_arr = None
status class-attribute instance-attribute
status = None
sub_industry class-attribute instance-attribute
sub_industry = None
value_segment class-attribute instance-attribute
value_segment = None

SearchAccountSortKey

Bases: AlanBaseEnum

Enum defining the possible sort keys for search accounts.

employee_count class-attribute instance-attribute
employee_count = 'employee_count'
loss_ratio_credibility class-attribute instance-attribute
loss_ratio_credibility = 'loss_ratio_credibility'
name class-attribute instance-attribute
name = 'name'
owner_name class-attribute instance-attribute
owner_name = 'owner_name'
signed_total_arr class-attribute instance-attribute
signed_total_arr = 'signed_total_arr'

search_accounts

search_accounts(
    page=1,
    per_page=100,
    app_name=None,
    name_or_id=None,
    account_ids=None,
    employees_count_more_than=None,
    employees_count_less_than=None,
    is_key_account=None,
    am_level=None,
    owner_name=None,
    value_segment=None,
    status=None,
    min_start_date_after=None,
    min_start_date_before=None,
    loss_ratio_credibility_more_than=None,
    loss_ratio_credibility_less_than=None,
    signed_total_arr_more_than=None,
    signed_total_arr_less_than=None,
    company_count_more_than=None,
    company_count_less_than=None,
    live_health_contract_count_more_than=None,
    live_health_contract_count_less_than=None,
    live_prevoyance_contract_count_more_than=None,
    live_prevoyance_contract_count_less_than=None,
    has_health_contract=None,
    has_prevoyance_contract=None,
    is_bookmarked_by_user_id=None,
    sort_by=None,
    sort_desc=False,
)

Search for accounts. Results contains aggregated data computed by Turing.

Source code in components/global_account/public/queries/search_accounts.py
def search_accounts(
    page: int = 1,
    per_page: int = 100,
    app_name: AppName | None = None,
    name_or_id: str | None = None,
    account_ids: list[UUID] | None = None,
    employees_count_more_than: int | None = None,
    employees_count_less_than: int | None = None,
    is_key_account: bool | None = None,
    am_level: str | None = None,
    owner_name: str | None = None,
    value_segment: str | None = None,
    status: AccountStatus | None = None,
    min_start_date_after: date | None = None,
    min_start_date_before: date | None = None,
    loss_ratio_credibility_more_than: float | None = None,
    loss_ratio_credibility_less_than: float | None = None,
    signed_total_arr_more_than: float | None = None,
    signed_total_arr_less_than: float | None = None,
    company_count_more_than: int | None = None,
    company_count_less_than: int | None = None,
    live_health_contract_count_more_than: int | None = None,
    live_health_contract_count_less_than: int | None = None,
    live_prevoyance_contract_count_more_than: int | None = None,
    live_prevoyance_contract_count_less_than: int | None = None,
    has_health_contract: bool | None = None,
    has_prevoyance_contract: bool | None = None,
    is_bookmarked_by_user_id: str | None = None,
    sort_by: SearchAccountSortKey | None = None,
    sort_desc: bool = False,
) -> Paginate[SearchAccount]:
    """
    Search for accounts. Results contains aggregated data computed by Turing.
    """
    from components.global_account.internal.models.bookmark_account import (
        BookmarkAccount,
    )

    query = current_session.query(TuringSearchAccountSQLA)  # noqa: ALN085
    query = query.options(joinedload(TuringSearchAccountSQLA.bookmarks))

    if app_name:
        query = query.filter(TuringSearchAccountSQLA.app_name == app_name)

    if name_or_id:
        search_str = unquote(name_or_id).strip().replace(" ", "%")
        query = query.filter(
            or_(
                unaccent(TuringSearchAccountSQLA.name).ilike(
                    unaccent(f"%{search_str}%")
                ),
                cast(TuringSearchAccountSQLA.id, Text).like(f"{search_str}%"),
            )
        )

    if account_ids:
        query = query.filter(TuringSearchAccountSQLA.id.in_(account_ids))

    if employees_count_more_than:
        query = query.filter(
            TuringSearchAccountSQLA.employee_count >= employees_count_more_than
        )

    if employees_count_less_than:
        query = query.filter(
            TuringSearchAccountSQLA.employee_count <= employees_count_less_than
        )

    if is_key_account is not None:
        query = query.filter(TuringSearchAccountSQLA.is_key_account == is_key_account)

    if am_level:
        query = query.filter(TuringSearchAccountSQLA.am_level == am_level)

    if owner_name:
        query = query.filter(
            func.unaccent(TuringSearchAccountSQLA.owner_name).ilike(
                func.unaccent(f"%{owner_name}%")
            )
        )

    if value_segment:
        query = query.filter(TuringSearchAccountSQLA.value_segment == value_segment)

    if min_start_date_after:
        query = query.filter(
            TuringSearchAccountSQLA.min_start_date >= min_start_date_after
        )

    if min_start_date_before:
        query = query.filter(
            TuringSearchAccountSQLA.min_start_date <= min_start_date_before
        )

    if status:
        query = query.filter(TuringSearchAccountSQLA.status == status)

    if loss_ratio_credibility_more_than:
        query = query.filter(
            TuringSearchAccountSQLA.loss_ratio_credibility
            >= loss_ratio_credibility_more_than
        )

    if loss_ratio_credibility_less_than:
        query = query.filter(
            TuringSearchAccountSQLA.loss_ratio_credibility
            <= loss_ratio_credibility_less_than
        )

    if signed_total_arr_more_than:
        query = query.filter(
            TuringSearchAccountSQLA.signed_total_arr >= signed_total_arr_more_than
        )

    if signed_total_arr_less_than:
        query = query.filter(
            TuringSearchAccountSQLA.signed_total_arr <= signed_total_arr_less_than
        )

    if company_count_more_than:
        query = query.filter(
            TuringSearchAccountSQLA.company_count >= company_count_more_than
        )

    if company_count_less_than:
        query = query.filter(
            TuringSearchAccountSQLA.company_count <= company_count_less_than
        )

    if live_health_contract_count_more_than:
        query = query.filter(
            TuringSearchAccountSQLA.live_health_contract_count
            >= live_health_contract_count_more_than
        )

    if live_health_contract_count_less_than:
        query = query.filter(
            TuringSearchAccountSQLA.live_health_contract_count
            <= live_health_contract_count_less_than
        )

    if live_prevoyance_contract_count_more_than:
        query = query.filter(
            TuringSearchAccountSQLA.live_prevoyance_contract_count
            >= live_prevoyance_contract_count_more_than
        )

    if live_prevoyance_contract_count_less_than:
        query = query.filter(
            TuringSearchAccountSQLA.live_prevoyance_contract_count
            <= live_prevoyance_contract_count_less_than
        )

    if has_health_contract is not None:
        if has_health_contract:
            query = query.filter(TuringSearchAccountSQLA.live_health_contract_count > 0)
        else:
            query = query.filter(
                or_(
                    TuringSearchAccountSQLA.live_health_contract_count == 0,
                    TuringSearchAccountSQLA.live_health_contract_count.is_(None),
                )
            )

    if has_prevoyance_contract is not None:
        if has_prevoyance_contract:
            query = query.filter(
                TuringSearchAccountSQLA.live_prevoyance_contract_count > 0
            )
        else:
            query = query.filter(
                or_(
                    TuringSearchAccountSQLA.live_prevoyance_contract_count == 0,
                    TuringSearchAccountSQLA.live_prevoyance_contract_count.is_(None),
                )
            )

    if is_bookmarked_by_user_id:
        query = query.join(
            BookmarkAccount,
            BookmarkAccount.account_id == TuringSearchAccountSQLA.id,
        ).filter(BookmarkAccount.user_id == is_bookmarked_by_user_id)

    # Apply sorting
    if sort_by:
        sort_column = {
            SearchAccountSortKey.name: TuringSearchAccountSQLA.name,
            SearchAccountSortKey.employee_count: (
                TuringSearchAccountSQLA.employee_count
            ),
            SearchAccountSortKey.owner_name: TuringSearchAccountSQLA.owner_name,
            SearchAccountSortKey.signed_total_arr: (
                TuringSearchAccountSQLA.signed_total_arr
            ),
            SearchAccountSortKey.loss_ratio_credibility: (
                TuringSearchAccountSQLA.loss_ratio_credibility
            ),
        }[sort_by]

        query = query.order_by(sort_column.desc() if sort_desc else sort_column.asc())
    else:
        query = query.order_by(TuringSearchAccountSQLA.name.asc())

    return Paginate.from_query(
        query=query,
        on_item=SearchAccount.from_model,
        page=page,
        per_page=per_page,
    )