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)
Source code in components/global_account/external/account.py
@tracer.wrap(service="global_account")
def get_account(account_id: UUID) -> Account:
    account = _get_sqla_account(account_id)

    entities = get_account_entities(account_id)
    ccns = get_ccns({entity.ccn_code for entity in entities if entity.ccn_code})
    account_history = get_account_settings_history(
        account_id=account_id, created_at=account.created_at
    )

    return Account(
        id=account.id,
        name=account.name,
        created_at=account.created_at,
        entities=entities,
        ccns=ccns,
        periods=account_history,
    )

get_account_entities

get_account_entities(account_id)
Source code in components/global_account/external/account.py
@tracer.wrap(service="global_account")
def get_account_entities(account_id: UUID) -> list[Entity]:
    account_repository = CompositeAccountRepository()
    account = _get_sqla_account(account_id)

    prospects = account_repository.search_builder_prospects(
        alan_account_id=str(account_id)
    )
    prospect_by_company_ref = {
        mandatory(prospect.company).id: prospect
        for prospect in prospects
        if prospect.company is not None
    }

    # Building entities from Builder Prospects
    entities: list[Entity] = [
        from_builder_prospect_to_entity(prospect) for prospect in prospects
    ]

    companies_without_builder_prospect = []
    entity_by_company_ref = {entity.company_ref: entity for entity in entities}

    for company in account.companies:
        # Aligning builder prospect & companies names for coherence
        if str(company.id) in prospect_by_company_ref:
            entity = entity_by_company_ref[str(company.id)]
            entity.name = company.name
            entity.formatted_name = company.display_name

        # Building entities from Companies not linked to any BuilderProspect
        else:
            companies_without_builder_prospect.append(
                CompanyEntityAPISchema(company_ref=str(company.id), prospect=None),
            )

    return entities + get_entities_from_companies(companies_without_builder_prospect)

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.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_builder_prospects_from_contract_versions module-attribute

get_or_create_builder_prospects_from_contract_versions = (
    get_or_create_builder_prospects_from_contract_versions
)

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,
    )
    from components.global_account.internal.queries.builder_prospect import (
        get_company_prospect_from_model,
    )

    query = current_session.query(BuilderProspect).filter(  # noqa: ALN085
        BuilderProspect.company_identifier == siren,
        BuilderProspect.industry_framework_agreement_code == ccn_code,
        BuilderProspect.specific_company_identifier == nic,
    )

    if is_from_self_serve is not None:
        query = query.filter(BuilderProspect.is_from_self_serve == is_from_self_serve)

    builder_prospect_model = query.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_from_model(builder_prospect_model)
        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 get_company_prospect_from_model(
        get_or_raise_missing_resource(BuilderProspectSQLA, 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:
    builder_prospect = get_resource_or_none(BuilderProspectSQLA, id)
    return (
        get_company_prospect_from_model(builder_prospect) if builder_prospect else None
    )

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: list[BuilderProspectSQLA] = list(
        current_session.scalars(
            select(BuilderProspectSQLA).where(BuilderProspectSQLA.id.in_(ids))
        ).all()
    )

    found_ids = {str(builder_prospect.id) for builder_prospect in builder_prospects}
    missing_ids = set(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 get_company_prospects_from_models(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_from_models(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 = CompositeAccountRepository().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):
        raise ValueError(f"Missing/extra prospects? {entities=} {relevant_prospects=}")

    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.
    """
    from components.global_account.internal.queries.builder_prospect import (
        get_company_prospects,
    )

    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

components.global_account.public.composite_account_repository

CompositeAccountRepository

Bases: AccountRepository

Single AccountRepository that will query across countries.

create_account

create_account(account)
Source code in components/global_account/public/composite_account_repository.py
@override
def create_account(self, account: Account) -> Account:
    match account.country:
        case GlobalAccountSupportedCountry.fr:
            return FrAccountRepository().create_account(account)
        case GlobalAccountSupportedCountry.be:
            return BeAccountRepository().create_account(account)
        case _:
            raise ValueError(
                f"create_account: Unsupported country: {account.country}"
            )

get_account

get_account(account_id)

Iteratively search through all countries and returns the relevant account

Source code in components/global_account/public/composite_account_repository.py
@override
def get_account(self, account_id: uuid.UUID) -> Account:
    """Iteratively search through all countries and returns the relevant account"""
    for repo in (FrAccountRepository(), BeAccountRepository()):
        try:
            account = repo.get_account(account_id)
            return account
        except Exception:
            current_logger.debug(f"Ignoring {type(repo)!r}")

    raise ValueError(f"Couldn't not find any {account_id=} in supported countries")

get_full_account

get_full_account(account_id)
Source code in components/global_account/public/composite_account_repository.py
@override
def get_full_account(self, account_id: uuid.UUID) -> FullAccount:
    for repo in (FrAccountRepository(), BeAccountRepository()):
        try:
            return repo.get_full_account(account_id)
        except Exception:
            current_logger.debug(f"Ignoring {type(repo)!r}")

    raise ValueError(f"Couldn't not find any {account_id=} in supported countries")

get_or_create_builder_prospects

get_or_create_builder_prospects(account_id)

Iteratively search through all countries and returns the relevant prospects

Source code in components/global_account/public/composite_account_repository.py
@override
def get_or_create_builder_prospects(
    self, account_id: uuid.UUID
) -> set[BuilderProspect]:
    """Iteratively search through all countries and returns the relevant prospects"""
    for repo in (FrAccountRepository(), BeAccountRepository()):
        try:
            prospects = repo.get_or_create_builder_prospects(account_id)

            if not prospects:
                continue

            current_session.commit()
            return prospects
        except Exception:
            current_logger.debug(f"Ignoring {type(repo)!r}")

    raise ValueError(f"Couldn't not find any {account_id=} in supported countries")

search_accounts

search_accounts(company_identifiers=None)

Iteratively search through all countries and returns the relevant accounts

Source code in components/global_account/public/composite_account_repository.py
@override
def search_accounts(
    self, company_identifiers: list[str] | None = None
) -> list[Account]:
    """Iteratively search through all countries and returns the relevant accounts"""
    for repo in (FrAccountRepository(), BeAccountRepository()):
        try:
            accounts = repo.search_accounts(company_identifiers=company_identifiers)

            if not accounts:
                continue

            return accounts
        except Exception:
            current_logger.debug(f"Ignoring {type(repo)!r}")

    raise ValueError(
        f"Couldn't not find any accounts for company_identifiers{company_identifiers=} in supported countries"
    )

search_companies

search_companies(account_id)
Source code in components/global_account/public/composite_account_repository.py
@override
def search_companies(self, account_id: uuid.UUID) -> set[Company]:
    for repo in (FrAccountRepository(), BeAccountRepository()):
        try:
            companies = repo.search_companies(account_id=account_id)

            if not companies:
                continue

            return companies
        except Exception:
            current_logger.debug(f"Ignoring {type(repo)!r}")

    raise ValueError(
        f"Couldn't not find any companies for account_id{account_id=} in supported countries"
    )

update_company

update_company(company)
Source code in components/global_account/public/composite_account_repository.py
@override
def update_company(self, company: Company) -> Company:
    match company.country:
        case GlobalAccountSupportedCountry.fr:
            return FrAccountRepository().update_company(company)
        case GlobalAccountSupportedCountry.be:
            return BeAccountRepository().update_company(company)
        case _:
            raise ValueError(
                f"update_company: Unsupported country: {company.country}"
            )

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'
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(
    siren,
    name,
    ccn_code,
    ape_code,
    postal_code,
    company_creation_year,
    number_of_primaries,
    country,
    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,
)

Bases: DataClassJsonMixin

alan_account_id class-attribute instance-attribute
alan_account_id = None
ape_code instance-attribute
ape_code
ccn_code instance-attribute
ccn_code
company_creation_year instance-attribute
company_creation_year
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
name instance-attribute
name
nic class-attribute instance-attribute
nic = None
number_of_primaries instance-attribute
number_of_primaries
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 instance-attribute
siren

PatchBuilderProspectAPISchema dataclass

PatchBuilderProspectAPISchema(
    siren=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,
)

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
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

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,
    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),
        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,
        company_creation_year=bp.creation_year,
        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,
    )
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

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,
    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
)

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 instance-attribute
company_identifier
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
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
salesforce_opportunity_id class-attribute instance-attribute
salesforce_opportunity_id = None
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
)

Bases: DataClassJsonMixin

Represents an actual Company

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
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'
fr class-attribute instance-attribute
fr = 'fr'

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"""
    country = SalesforceAccountCountry.validate(account.country)

    match country:
        case SalesforceAccountCountry.fr:
            return GlobalAccountSupportedCountry.fr
        case SalesforceAccountCountry.be:
            return GlobalAccountSupportedCountry.be
        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 _:
            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,
    )