Skip to content

Api reference

components.shop.public.auth

saleor_alaner_authorization

Authorization strategy for shop endpoints called from the Saleor Dashboard.

Flow: 1. Frontend (shop-saleor-app) forwards the Saleor AppBridge JWT in the X-Saleor-App-Token header. 2. authenticator verifies the JWT signature against the cached Saleor JWKS (refresh-on-failure), validates iss/is_staff/exp, and stashes claims on g. 3. authorize looks up the active AlanEmployee by alan_email matching the JWT's email claim, then stashes the resolved user on g.current_user. Result cached per email (1h TTL).

The email lookup (vs. an OIDC/keycloak chain) keeps the strategy portable across prod / staging / per-developer envs where keycloak ids differ but the Alaner email stays the same.

On any failure: raises BaseErrorCode.authorization_error (HTTP 401).

SaleorAlanerAuthorizationStrategy

SaleorAlanerAuthorizationStrategy()

Bases: BaseAuthorizationStrategy

Restrict access to Alaners authenticated via the Saleor Dashboard.

Source code in components/shop/public/auth/saleor_alaner_authorization.py
def __init__(self) -> None:
    super().__init__(allow_deep_link=False)
authentication_required class-attribute instance-attribute
authentication_required = False
authenticator
authenticator()

Verify the Saleor AppBridge JWT and stash claims on g.

Source code in components/shop/public/auth/saleor_alaner_authorization.py
def authenticator(
    self,
) -> Callable[[Callable[_P, _T]], Callable[_P, _T]]:
    """Verify the Saleor AppBridge JWT and stash claims on ``g``."""

    def endpoint_decorator(endpoint_fn: Callable[_P, _T]) -> Callable[_P, _T]:
        @wraps(endpoint_fn)
        def decorated_endpoint(*args: _P.args, **kwargs: _P.kwargs) -> _T:
            token = request.headers.get(_HEADER_NAME, "")
            if not token:
                current_logger.info(
                    "Shop: [Saleor] Missing X-Saleor-App-Token header",
                    path=request.path,
                )
                raise BaseErrorCode.authorization_error(
                    message="Missing Saleor app token",
                    http_code=401,
                )

            claims = verify_app_bridge_jwt(token)
            if claims is None:
                raise BaseErrorCode.authorization_error(
                    message="Invalid Saleor app token",
                    http_code=401,
                )

            g.saleor_app_bridge_claims = claims
            return endpoint_fn(*args, **kwargs)

        return cast("Callable[_P, _T]", decorated_endpoint)

    return endpoint_decorator
authorize
authorize(Controller, method_name, call_args, call_kwargs)

Resolve the Saleor staff user to an Alaner and gate the request.

Source code in components/shop/public/auth/saleor_alaner_authorization.py
@contextmanager
def authorize(self, Controller, method_name, call_args, call_kwargs):  # type: ignore[no-untyped-def]  # noqa: ARG002
    """Resolve the Saleor staff user to an Alaner and gate the request."""
    with self._ensure_custom_authorization():
        claims = getattr(g, "saleor_app_bridge_claims", None)
        if not claims:
            raise BaseErrorCode.authorization_error(
                message="Missing Saleor app token",
                http_code=401,
            )

        email = claims.get("email")
        if not email:
            current_logger.info("Shop: [Saleor] AppBridge JWT missing email claim")
            raise BaseErrorCode.authorization_error(
                message="Invalid Saleor app token",
                http_code=401,
            )

        user = _resolve_alaner(email)
        if user is None:
            raise BaseErrorCode.authorization_error(
                message="Saleor user is not an Alaner",
                http_code=401,
            )

        g.current_user = user
        g.current_alan_employee = user.alan_employee
        yield
ensure_custom_authorization class-attribute instance-attribute
ensure_custom_authorization = False

components.shop.public.business_logic

favorites

Public re-exports for cross-component callers (e.g. eyewear dual-write).

create_favorite_for_feature_user

create_favorite_for_feature_user(feature_user, sku)
Source code in components/shop/internal/business_logic/favorites/favorite.py
def create_favorite_for_feature_user(feature_user: FeatureUser, sku: str) -> None:
    shop_user = get_or_create_shop_user(feature_user=feature_user)
    current_logger.info(
        "Shop favorites: creating favorite", shop_user_id=shop_user.id, sku=sku
    )

    stmt = (
        insert(ShopFavorite)
        .values(shop_user_id=shop_user.id, sku=sku)
        .on_conflict_do_nothing(constraint=ShopFavorite.UNIQUE_USER_SKU_CONSTRAINT)
    )
    result = current_session.execute(stmt)
    current_session.commit()

    current_logger.info(
        "Shop favorites: created favorite",
        shop_user_id=shop_user.id,
        sku=sku,
        was_created=result.rowcount == 1,
    )

delete_favorite_for_feature_user

delete_favorite_for_feature_user(feature_user, sku)
Source code in components/shop/internal/business_logic/favorites/favorite.py
def delete_favorite_for_feature_user(feature_user: FeatureUser, sku: str) -> None:
    shop_user = get_or_create_shop_user(feature_user=feature_user)
    current_logger.info(
        "Shop favorites: deleting favorite", shop_user_id=shop_user.id, sku=sku
    )

    stmt = delete(ShopFavorite).where(
        ShopFavorite.shop_user_id == shop_user.id, ShopFavorite.sku == sku
    )
    result = current_session.execute(stmt)
    current_session.commit()

    current_logger.info(
        "Shop favorites: favorite removed",
        shop_user_id=shop_user.id,
        sku=sku,
        was_deleted=result.rowcount > 0,
    )

components.shop.public.commands

app_group

shop_commands module-attribute

shop_commands = AppGroup('shop')

backfill_shop_visits

backfill_shop_visits

backfill_shop_visits(dry_run)

Backfill shop visits from Segment.

Source code in components/shop/public/commands/backfill_shop_visits.py
@shop_commands.command()
@command_with_dry_run
def backfill_shop_visits(dry_run: bool) -> None:
    """Backfill shop visits from Segment."""
    from components.shop.internal.business_logic.users.backfill_shop_visits import (
        backfill_shop_visits as backfill_shop_visits_action,
    )

    backfill_shop_visits_action(dry_run=dry_run)

global_invoicing

process_contact_lens_invoicing

process_contact_lens_invoicing(
    invoicing_date, app_id, dry_run
)

Process the contact lens invoicing for a given period.

Source code in components/shop/public/commands/global_invoicing.py
@shop_commands.command()
@click.option("--invoicing-date", type=DateCli(), required=True)
@click.option(
    "--app-id",
    type=str,
    required=True,
    default=AppName.ALAN_FR,
    help="App ID",
)
@command_with_dry_run
def process_contact_lens_invoicing(
    invoicing_date: date, app_id: str, dry_run: bool
) -> None:
    """Process the contact lens invoicing for a given period."""
    try:
        saleor_helper = SaleorHelper()
        # retrieve transactions to invoice
        transactions_to_invoice = (
            saleor_helper.retrieve_transactions_to_invoice_for_period(
                usage_type=UsageType.contact_lens,
                period=invoicing_date,
            )
        )

        # generate global invoice
        validated_app_id = AppName.validate(app_id)
        shop_adapter = get_app_specific_shop_adapter(app_id=validated_app_id)

        order_invoice = shop_adapter.generate_contact_lens_global_invoice(
            transactions_to_invoice=transactions_to_invoice,
            period=invoicing_date,
            is_preview=dry_run,
        )

        current_logger.info(
            "Shop: [global_invoicing] Contact lens global invoice generated",
            invoice_url=order_invoice,
        )

    except Exception as e:
        current_logger.error(
            "Shop: [global_invoicing] Error generating contact lens global invoice",
            error=e,
        )
        raise e

import_eyewear_frames_to_saleor

import_eyewear_frames_to_saleor

import_eyewear_frames_to_saleor(
    source_csv, prod, limit, publish, dry_run
)

Import eyewear frames from a CSV export into Saleor as Products + Variants.

Source code in components/shop/public/commands/import_eyewear_frames_to_saleor.py
@shop_commands.command()
@click.option(
    "--source-csv",
    "source_csv",
    type=click.Path(exists=True, dir_okay=False, readable=True),
    required=True,
    help="Path to a CSV export of the Alan EyewearFrame table (downloaded from the EU tools admin UI).",
)
@click.option(
    "--prod",
    is_flag=True,
    default=False,
    help="Target the production Saleor store; must match the configured SALEOR_GRAPHQL_URL.",
)
@click.option(
    "--limit",
    type=int,
    default=None,
    help="Max number of frames to process (counts FRAMES, not groups). Useful for smoke-testing.",
)
@click.option(
    "--publish",
    is_flag=True,
    default=False,
    help="Publish created products on the channel; default leaves them unpublished.",
)
@command_with_dry_run
def import_eyewear_frames_to_saleor(
    source_csv: str,
    prod: bool,
    limit: int | None,
    publish: bool,
    dry_run: bool,
) -> None:
    """Import eyewear frames from a CSV export into Saleor as Products + Variants."""
    assert_saleor_credentials_configured()

    from components.shop.internal.business_logic.eyewear_frames_saleor.importer import (
        import_eyewear_frames_to_saleor as import_action,
    )

    import_action(
        dry_run=dry_run,
        prod=prod,
        source_csv_path=source_csv,
        limit=limit,
        publish=publish,
    )

supplier_action

check_missing_skus

check_missing_skus(dry_run)

Cross-check every active Saleor SKU against the supplier's Shopify catalog.

Variants whose SKU is missing on Shopify cause the parent product to be unpublished on French channels. Transient Shopify failures do NOT trigger an unpublish; they are surfaced in the Slack alert so ops can re-check manually.

Source code in components/shop/public/commands/supplier_action.py
@shop_commands.command()
@command_with_dry_run
def check_missing_skus(dry_run: bool) -> None:
    """Cross-check every active Saleor SKU against the supplier's Shopify catalog.

    Variants whose SKU is missing on Shopify cause the parent product to be
    unpublished on French channels. Transient Shopify failures do NOT trigger an
    unpublish; they are surfaced in the Slack alert so ops can re-check manually.
    """
    from components.shop.internal.business_logic.suppliers.sku_audit import (
        run_sku_audit,
        send_sku_audit_alert,
    )

    report = run_sku_audit(dry_run=dry_run)
    send_sku_audit_alert(report, dry_run=dry_run)

daily_routine

daily_routine(dry_run, supplier)
Source code in components/shop/public/commands/supplier_action.py
@shop_commands.command()
@command_with_dry_run
@click.option(
    "--supplier",
    required=False,
    default="",
    help="slug of the supplier to run the 'daily_routine' action for",
)
def daily_routine(  # noqa: D103
    dry_run: bool,
    supplier: str,
) -> None:
    from components.shop.internal.business_logic.suppliers.supplier_action_manager import (
        SupplierActionManager,
    )

    SupplierActionManager(dry_run=dry_run).run_action(
        action="daily_routine",
        for_supplier_only=supplier if supplier else None,
    )

fulfill_orders

fulfill_orders(dry_run, supplier)
Source code in components/shop/public/commands/supplier_action.py
@shop_commands.command()
@command_with_dry_run
@click.option(
    "--supplier",
    required=False,
    default="",
    help="slug of the supplier to run the 'fulfill_orders' action for",
)
def fulfill_orders(  # noqa: D103
    dry_run: bool,
    supplier: str,
) -> None:
    from components.shop.internal.business_logic.suppliers.supplier_action_manager import (
        SupplierActionManager,
    )

    SupplierActionManager(dry_run=dry_run).run_action(
        action="fulfill_orders",
        for_supplier_only=supplier if supplier else None,
    )

run_supplier_action

run_supplier_action(dry_run, action, supplier, days=None)
Source code in components/shop/public/commands/supplier_action.py
@shop_commands.command()
@command_with_dry_run
@click.option(
    "--action",
    is_flag=False,
    required=True,
    default=None,
    help="be it fulfill_orders, update_recent_orders, update_draft_orders, update_trackings or synchronize_stocks, synchronize_catalog, supplier_routine or all",
)
@click.option(
    "--supplier",
    required=False,
    default="",
    help="slug of the supplier to run the action for",
)
@click.option(
    "--days",
    required=False,
    type=int,
    help="number of days used for the update_recent_orders action",
)
def run_supplier_action(  # noqa: D103
    dry_run: bool,
    action: str,
    supplier: str,
    days: Optional[int] = None,
) -> None:
    from components.shop.internal.business_logic.suppliers.supplier_action_manager import (
        SupplierAction,
        SupplierActionManager,
    )

    extra_args = {}
    if days is not None:
        extra_args["days"] = days

    if action == "all":
        for action_enum in SupplierAction:
            SupplierActionManager(dry_run=dry_run).run_action(
                action=action_enum.value,
                for_supplier_only=supplier if supplier else None,
                extra_args=extra_args,
            )
    else:
        SupplierActionManager(dry_run=dry_run).run_action(
            action=action,
            for_supplier_only=supplier if supplier else None,
            extra_args=extra_args,
        )

sync_delivery_status

sync_delivery_status(dry_run)
Source code in components/shop/public/commands/supplier_action.py
@shop_commands.command()
@command_with_dry_run
def sync_delivery_status(  # noqa: D103
    dry_run: bool,
) -> None:
    from components.shop.internal.business_logic.suppliers.supplier_action_manager import (
        SupplierActionManager,
    )

    SupplierActionManager(dry_run=dry_run).run_action(
        action="sync_delivery_status",
    )

synchronize_catalog

synchronize_catalog(dry_run, supplier)
Source code in components/shop/public/commands/supplier_action.py
@shop_commands.command()
@command_with_dry_run
@click.option(
    "--supplier",
    required=False,
    default="",
    help="slug of the supplier to run the 'synchronize_catalog' action for",
)
def synchronize_catalog(  # noqa: D103
    dry_run: bool,
    supplier: str,
) -> None:
    from components.shop.internal.business_logic.suppliers.supplier_action_manager import (
        SupplierActionManager,
    )

    SupplierActionManager(dry_run=dry_run).run_action(
        action="synchronize_catalog",
        for_supplier_only=supplier if supplier else None,
    )

synchronize_stocks

synchronize_stocks(dry_run, supplier)
Source code in components/shop/public/commands/supplier_action.py
@shop_commands.command()
@command_with_dry_run
@click.option(
    "--supplier",
    required=False,
    default="",
    help="slug of the supplier to run the 'synchronize_stocks' action for",
)
def synchronize_stocks(  # noqa: D103
    dry_run: bool,
    supplier: str,
) -> None:
    from components.shop.internal.business_logic.suppliers.supplier_action_manager import (
        SupplierActionManager,
    )

    SupplierActionManager(dry_run=dry_run).run_action(
        action="synchronize_stocks",
        for_supplier_only=supplier if supplier else None,
    )

update_draft_orders

update_draft_orders(dry_run, supplier)
Source code in components/shop/public/commands/supplier_action.py
@shop_commands.command()
@command_with_dry_run
@click.option(
    "--supplier",
    required=False,
    default="",
    help="slug of the supplier to run the 'update_draft_orders' action for",
)
def update_draft_orders(  # noqa: D103
    dry_run: bool,
    supplier: str,
) -> None:
    from components.shop.internal.business_logic.suppliers.supplier_action_manager import (
        SupplierActionManager,
    )

    SupplierActionManager(dry_run=dry_run).run_action(
        action="update_draft_orders",
        for_supplier_only=supplier if supplier else None,
    )

update_orders_by_numbers

update_orders_by_numbers(dry_run, supplier, order_numbers)
Source code in components/shop/public/commands/supplier_action.py
@shop_commands.command()
@command_with_dry_run
@click.option(
    "--supplier",
    required=False,
    default="",
    help="slug of the supplier to run the 'update_orders_by_numbers' action for",
)
@click.option(
    "--order-numbers",
    required=True,
    type=str,
    help="comma-separated order numbers to update (e.g. --order-numbers 123,456)",
)
def update_orders_by_numbers(  # noqa: D103
    dry_run: bool,
    supplier: str,
    order_numbers: str,
) -> None:
    from components.shop.internal.business_logic.suppliers.supplier_action_manager import (
        SupplierActionManager,
    )

    parsed_order_numbers = [
        int(n.strip()) for n in order_numbers.split(",") if n.strip()
    ]
    SupplierActionManager(dry_run=dry_run).run_action(
        action="update_orders_by_numbers",
        for_supplier_only=supplier if supplier else None,
        extra_args={"order_numbers": parsed_order_numbers},
    )

update_recent_orders

update_recent_orders(dry_run, supplier, days=None)
Source code in components/shop/public/commands/supplier_action.py
@shop_commands.command()
@command_with_dry_run
@click.option(
    "--supplier",
    required=False,
    default="",
    help="slug of the supplier to run the 'update_recent_orders' action for",
)
@click.option(
    "--days",
    required=False,
    type=int,
    help="number of days used for the 'update_recent_orders' action",
)
def update_recent_orders(  # noqa: D103
    dry_run: bool,
    supplier: str,
    days: Optional[int] = None,
) -> None:
    from components.shop.internal.business_logic.suppliers.supplier_action_manager import (
        SupplierActionManager,
    )

    extra_args = {}
    if days is not None:
        extra_args["days"] = days

        SupplierActionManager(dry_run=dry_run).run_action(
            action="update_recent_orders",
            for_supplier_only=supplier if supplier else None,
            extra_args=extra_args,
        )

update_trackings

update_trackings(dry_run, supplier)
Source code in components/shop/public/commands/supplier_action.py
@shop_commands.command()
@command_with_dry_run
@click.option(
    "--supplier",
    required=False,
    default="",
    help="slug of the supplier to run the 'update_trackings' action for",
)
def update_trackings(  # noqa: D103
    dry_run: bool,
    supplier: str,
) -> None:
    from components.shop.internal.business_logic.suppliers.supplier_action_manager import (
        SupplierActionManager,
    )

    SupplierActionManager(dry_run=dry_run).run_action(
        action="update_trackings",
        for_supplier_only=supplier if supplier else None,
    )

sync_product_recommendations

sync_product_recommendations

sync_product_recommendations(dry_run)

Synchronize product recommendations on Saleor.

Source code in components/shop/public/commands/sync_product_recommendations.py
@shop_commands.command()
@command_with_dry_run
def sync_product_recommendations(dry_run: bool) -> None:
    """Synchronize product recommendations on Saleor."""
    from components.shop.internal.business_logic.recommendations.sync_product_recommendations import (
        sync_product_recommendations as sync_product_recommendations_action,
    )

    sync_product_recommendations_action(dry_run=dry_run)

sync_saleor_channels

sync_saleor_channels

sync_saleor_channels(from_channel, to_channel, dry_run)

Synchronize two Saleor channels

Source code in components/shop/public/commands/sync_saleor_channels.py
@shop_commands.command()
@click.option(
    "--from-channel",
    required=True,
    help="slug of the source channel",
)
@click.option(
    "--to-channel",
    required=True,
    help="slug of the destination channel",
)
@command_with_dry_run
def sync_saleor_channels(from_channel: str, to_channel: str, dry_run: bool) -> None:
    """Synchronize two Saleor channels"""
    from components.shop.internal.business_logic.channels.sync_saleor_channels import (
        sync_saleor_channels as sync_saleor_channels_action,
    )

    sync_saleor_channels_action(
        from_channel_slug=from_channel,
        to_channel_slug=to_channel,
        dry_run=dry_run,
    )

components.shop.public.const

REFUNDABLE_FULFILLMENT_STATUSES module-attribute

REFUNDABLE_FULFILLMENT_STATUSES = [
    RETURNED,
    REFUNDED_AND_RETURNED,
    CANCELED,
]

SHOP_EMAIL module-attribute

SHOP_EMAIL = 'shop@alan.eu'

SHOP_EMAIL_TEMPLATE module-attribute

SHOP_EMAIL_TEMPLATE = 'shop+{supplier_slug}@alan.eu'

UNTRACKED_STOCK module-attribute

UNTRACKED_STOCK = 999

components.shop.public.controllers

admin_cancellation_mail

admin_cancellation_mail_blp module-attribute

admin_cancellation_mail_blp = CustomBlueprint(
    "shop_admin_cancellation_mail",
    __name__,
    url_prefix="/api/shop-admin",
)

admin_order

ApproveFulfillmentPostJsonSchema

Bases: Schema

JSON body for POST approve fulfillment.

Generic across product families — for product-specific metadata (e.g. the lens the optician picked for a glasses order) use a per-family endpoint such as /approve-glasses which translates typed fields into the opaque fulfillment_metadata accepted by approve_or_cancel_fulfillment.

decision_reason class-attribute instance-attribute
decision_reason = Str(required=False, allow_none=True)

ApproveGlassesFulfillmentPostJsonSchema

Bases: Schema

JSON body for POST approve-glasses — the glasses-specific approve flow.

Wraps the generic approve flow with the SKU of the lens variant the optician picked in the dashboard modal. Persisted as fulfillment metadata under the optician-selected-lens-sku key.

Richer audit info (variant name + Saleor product URL) is recorded as a Saleor order event written client-side from the modal — keeps that content out of the Alan backend.

lens_variant_sku class-attribute instance-attribute
lens_variant_sku = Str(required=True)

CancelFulfillmentPostJsonSchema

Bases: Schema

JSON body for POST cancel fulfillment.

decision_reason class-attribute instance-attribute
decision_reason = Str(required=False, allow_none=True)
mail_body class-attribute instance-attribute
mail_body = Str(required=True)
mail_subject class-attribute instance-attribute
mail_subject = Str(required=True)

HelpFulfillmentPostJsonSchema

Bases: Schema

JSON body for POST help on a Saleor fulfillment.

message class-attribute instance-attribute
message = Str(required=True)

UploadInvoicePostFilesSchema

Bases: Schema

Files for invoice upload (multipart/form-data).

invoice class-attribute instance-attribute
invoice = Raw(required=True, allow_none=False)

admin_order_blp module-attribute

admin_order_blp = CustomBlueprint(
    "shop_admin_order",
    __name__,
    url_prefix="/api/shop-admin",
)

admin_testimonial

TestimonialPayload dataclass

TestimonialPayload(
    testimonial=None,
    internal_comment=None,
    alan_answer=None,
    is_reviewed=False,
    is_visible_from_product=False,
)

Bases: DataClassJsonMixin

Data class for admin testimonial payload.

alan_answer class-attribute instance-attribute
alan_answer = None
internal_comment class-attribute instance-attribute
internal_comment = None
is_reviewed class-attribute instance-attribute
is_reviewed = False
is_visible_from_product class-attribute instance-attribute
is_visible_from_product = False
testimonial class-attribute instance-attribute
testimonial = None

admin_testimonial_blp module-attribute

admin_testimonial_blp = CustomBlueprint(
    "shop_admin_testimonial",
    __name__,
    url_prefix="/api/shop-admin",
)

admin_vouchers

CreateAdminVoucherPostJsonSchema

Bases: Schema

JSON body for POST /admin/vouchers.

app_user_id class-attribute instance-attribute
app_user_id = Str(required=True)
expires_at class-attribute instance-attribute
expires_at = DateTime(
    required=False, load_default=_default_expires_at
)
voucher_code class-attribute instance-attribute
voucher_code = Enum(
    VoucherCode, by_value=True, required=True
)

ListAdminVouchersGetQuerySchema

Bases: Schema

Query params for GET /shop-admin/vouchers.

app_user_id class-attribute instance-attribute
app_user_id = Str(required=True)

admin_vouchers_blp module-attribute

admin_vouchers_blp = CustomBlueprint(
    "shop_admin_vouchers",
    __name__,
    url_prefix="/api/shop-admin",
)

intercom

CreateConversationForFulfillmentPostJsonSchema

Bases: Schema

Schema for POST /create_conversation_for_fulfillment.

fulfillment_id class-attribute instance-attribute
fulfillment_id = Str(
    required=True,
    validate=OwnerController(NoOwner),
    metadata={"description": "The ID of the fulfillment"},
)
intercom_space class-attribute instance-attribute
intercom_space = Enum(
    IntercomSpace,
    by_value=True,
    required=True,
    validate=OwnerController(NoOwner),
    metadata={
        "description": "The Intercom space to use (shop or care)"
    },
)
operator_full_name class-attribute instance-attribute
operator_full_name = Str(
    required=True,
    validate=OwnerController(NoOwner),
    metadata={
        "description": "The full name of Saleor connected user"
    },
)
order_id class-attribute instance-attribute
order_id = Str(
    required=True,
    validate=OwnerController(NoOwner),
    metadata={"description": "The ID of the order"},
)
template_id class-attribute instance-attribute
template_id = Str(
    required=True,
    validate=OwnerController(NoOwner),
    metadata={"description": "The ID of the template"},
)

intercom_blp module-attribute

intercom_blp = SaleorExtensionBlueprint(
    "shop_intercom",
    __name__,
    url_prefix="/api/shop-saleor-extension/intercom",
    auth_context_providers=[
        SaleorAppBridgeAuthContextProvider()
    ],
)

invoice_documents

GetInvoiceDocumentsGetQuerySchema

Bases: Schema

Schema for GET /invoice_documents.

document_ids class-attribute instance-attribute
document_ids = DelimitedList(Str(), required=True)

shop_invoice_documents_blp module-attribute

shop_invoice_documents_blp = CustomBlueprint(
    "shop_invoice_documents",
    __name__,
    url_prefix="/api/shop/invoice_documents",
)

invoicing

InvoicingSchema

Bases: Schema

Schema for POST /generate_and_upload_invoice and GET /retrieve_transactions_to_invoice.

app_id class-attribute instance-attribute
app_id = Enum(required=True, enum=AppName, by_value=True)
period class-attribute instance-attribute
period = Date(required=True, format='%Y-%m-%d')
unique_id class-attribute instance-attribute
unique_id = Str(required=True)
usage_type class-attribute instance-attribute
usage_type = Enum(required=True, enum=UsageType)

shop_invoicing_blp module-attribute

shop_invoicing_blp = SaleorExtensionBlueprint(
    "shop_invoicing",
    __name__,
    url_prefix="/api/shop-saleor-extension/invoicing",
    auth_context_providers=[
        SaleorAppBridgeAuthContextProvider()
    ],
)

saleor_admin_token

saleor_admin_token_blp module-attribute

saleor_admin_token_blp = CustomBlueprint(
    "shop_admin_saleor_token",
    __name__,
    url_prefix="/api/shop-admin",
)

saleor_checkout

CheckPrescriptionContentPostJsonSchema

Bases: Schema

Schema for check_prescription_content POST endpoint.

prescription_content class-attribute instance-attribute
prescription_content = Dict(required=True)
prescription_type class-attribute instance-attribute
prescription_type = Str(required=False, allow_none=True)

shop_saleor_checkouts_blp module-attribute

shop_saleor_checkouts_blp = CustomBlueprint(
    "shop_saleor_checkouts",
    __name__,
    url_prefix="/api/shop/saleor_checkouts",
)

saleor_manifest

FULFILLMENT_EVENTS_SUBSCRIPTION_QUERY module-attribute

FULFILLMENT_EVENTS_SUBSCRIPTION_QUERY = "subscription {\n  event {\n    ... on FulfillmentApproved {\n      __typename\n      order {\n        id\n      }\n      fulfillment {\n        id\n      }\n    }\n    ... on FulfillmentCanceled {\n      __typename\n      order {\n        id\n      }\n      fulfillment {\n        id\n      }\n    }\n  }\n}\n"

ORDER_EVENTS_SUBSCRIPTION_QUERY module-attribute

ORDER_EVENTS_SUBSCRIPTION_QUERY = "subscription {\n  event {\n    ... on OrderCancelled {\n      __typename\n      order {\n        id\n      }\n    }\n    ... on OrderConfirmed {\n      __typename\n      order {\n        id\n      }\n    }\n    ... on OrderCreated {\n      __typename\n      order {\n        id\n      }\n    }\n    ... on OrderFulfilled {\n      __typename\n      order {\n        id\n      }\n    }\n    ... on OrderUpdated {\n      __typename\n      order {\n        id\n      }\n    }\n    ... on OrderRefunded {\n      __typename\n      order {\n        id\n      }\n    }\n    ... on OrderPaid {\n      __typename\n      order {\n        id\n      }\n    }\n    ... on OrderMetadataUpdated {\n      __typename\n      order {\n        id\n      }\n    }\n    ... on OrderFullyRefunded {\n      __typename\n      order {\n        id\n      }\n    }\n    ... on OrderFullyPaid {\n      __typename\n      order {\n        id\n      }\n    }\n    ... on OrderExpired {\n      __typename\n      order {\n        id\n      }\n    }\n  }\n}\n"

PAYMENT_EVENTS_SUBSCRIPTION_QUERY module-attribute

PAYMENT_EVENTS_SUBSCRIPTION_QUERY = "subscription {\n  event {\n    ... on TransactionInitializeSession {\n      __typename\n      transaction {\n        ...SaleorTransactionItem\n      }\n      action {\n        ...SaleorTransactionProcessAction\n      }\n      sourceObject {\n        __typename\n        ...SaleorOrder\n        ...SaleorCheckout\n      }\n    }\n    ... on TransactionProcessSession {\n      __typename\n      transaction {\n        ...SaleorTransactionItem\n      }\n      action {\n        ...SaleorTransactionProcessAction\n      }\n    }\n  }\n}\n\nfragment SaleorTransactionItem on TransactionItem {\n  id\n  pspReference\n}\n\nfragment SaleorTransactionProcessAction on TransactionProcessAction {\n  actionType\n  amount\n  currency\n}\n\nfragment SaleorOrder on Order {\n  id\n}\n\nfragment SaleorCheckout on Checkout {\n  id\n}\n"

PERMISSIONS module-attribute

PERMISSIONS = [
    "HANDLE_CHECKOUTS",
    "HANDLE_PAYMENTS",
    "HANDLE_TAXES",
    "IMPERSONATE_USER",
    "MANAGE_CHANNELS",
    "MANAGE_CHECKOUTS",
    "MANAGE_DISCOUNTS",
    "MANAGE_GIFT_CARD",
    "MANAGE_MENUS",
    "MANAGE_OBSERVABILITY",
    "MANAGE_ORDERS",
    "MANAGE_ORDERS_IMPORT",
    "MANAGE_PAGES",
    "MANAGE_PAGE_TYPES_AND_ATTRIBUTES",
    "MANAGE_PLUGINS",
    "MANAGE_PRODUCTS",
    "MANAGE_PRODUCT_TYPES_AND_ATTRIBUTES",
    "MANAGE_SETTINGS",
    "MANAGE_SHIPPING",
    "MANAGE_STAFF",
    "MANAGE_TAXES",
    "MANAGE_TRANSLATIONS",
    "MANAGE_USERS",
]

TRANSACTION_EVENTS_SUBSCRIPTION_QUERY module-attribute

TRANSACTION_EVENTS_SUBSCRIPTION_QUERY = "subscription {\n  event {\n    ... on TransactionCancelationRequested {\n      __typename\n      transaction {\n        ...SaleorTransactionItem\n      }\n      action {\n        ...SaleorTransactionAction\n      }\n    }\n    ... on TransactionChargeRequested {\n      __typename\n      transaction {\n        ...SaleorTransactionItem\n      }\n      action {\n        ...SaleorTransactionAction\n      }\n    }\n    ... on TransactionRefundRequested {\n      __typename\n      transaction {\n        ...SaleorTransactionItem\n      }\n      action {\n        ...SaleorTransactionAction\n      }\n    }\n  }\n}\n\nfragment SaleorTransactionItem on TransactionItem {\n  id\n  pspReference\n}\n\nfragment SaleorTransactionAction on TransactionAction {\n  actionType\n  amount\n  currency\n}\n"

shop_saleor_manifest_blp module-attribute

shop_saleor_manifest_blp = CustomBlueprint(
    "shop_saleor_manifest",
    __name__,
    url_prefix="/api/shop/saleor_manifest",
    auth_context_providers=[AnonymousAuthContextProvider()],
)

saleor_register

shop_saleor_register_blp module-attribute

shop_saleor_register_blp = CustomBlueprint(
    "shop_saleor_register",
    __name__,
    url_prefix="/api/shop/saleor_register",
    auth_context_providers=[AnonymousAuthContextProvider()],
)

saleor_webhook

SaleorFulfillmentEventsPostJsonSchema

Bases: _SaleorWebhookBaseSchema

fulfillment class-attribute instance-attribute
fulfillment = Dict(
    required=True,
    metadata={"description": "Saleor fulfillment"},
)
order class-attribute instance-attribute
order = Dict(
    required=True, metadata={"description": "Saleor order"}
)

SaleorOrderEventsPostJsonSchema

Bases: _SaleorWebhookBaseSchema

order class-attribute instance-attribute
order = Dict(
    required=True, metadata={"description": "Saleor order"}
)

SaleorPaymentEventsPostJsonSchema

Bases: _SaleorWebhookBaseSchema

action class-attribute instance-attribute
action = Dict(
    required=False,
    load_default={},
    metadata={"description": "action of the event"},
)
sourceObject class-attribute instance-attribute
sourceObject = Dict(
    required=False,
    load_default={},
    metadata={
        "description": "could be either an order or a checkout"
    },
)
transaction class-attribute instance-attribute
transaction = Dict(
    required=True,
    metadata={"description": "transaction of the event"},
)

SaleorTransactionEventsPostJsonSchema

Bases: _SaleorWebhookBaseSchema

action class-attribute instance-attribute
action = Dict(
    required=True,
    metadata={"description": "action of the event"},
)
transaction class-attribute instance-attribute
transaction = Dict(
    required=True,
    metadata={"description": "transaction of the event"},
)

shop_saleor_webhooks_blp module-attribute

shop_saleor_webhooks_blp = CustomBlueprint(
    "shop_saleor_webhooks",
    __name__,
    url_prefix="/api/shop/saleor_webhooks",
    auth_context_providers=[AnonymousAuthContextProvider()],
)

saleor_webhooks_config

shop_saleor_webhooks_config_blp module-attribute

shop_saleor_webhooks_config_blp = CustomBlueprint(
    "shop_saleor_webhooks_config",
    __name__,
    url_prefix="/api/shop/saleor_webhooks_config",
    auth_context_providers=[AnonymousAuthContextProvider()],
)

shop_favorites

ShopFavoritesDeleteQueryArgs dataclass

ShopFavoritesDeleteQueryArgs(sku)

Args for DELETE /favorites — sku passed as query param so SKUs containing '/' are safe.

sku instance-attribute
sku

ShopFavoritesDeleteQuerySchema module-attribute

ShopFavoritesDeleteQuerySchema = class_schema(
    ShopFavoritesDeleteQueryArgs
)

ShopFavoritesPostJsonArgs dataclass

ShopFavoritesPostJsonArgs(sku)

Args for POST /favorites.

sku instance-attribute
sku

ShopFavoritesPostJsonSchema module-attribute

ShopFavoritesPostJsonSchema = class_schema(
    ShopFavoritesPostJsonArgs
)

shop_favorites_blp module-attribute

shop_favorites_blp = CustomBlueprint(
    "shop_favorites",
    __name__,
    url_prefix="/api/shop/favorites",
)

shop_user

GetShopUserGetQuerySchema

Bases: Schema

Schema for GET /shop_user.

update_visit class-attribute instance-attribute
update_visit = Bool(load_default=True)

UpdateShopUserPatchJsonSchema

Bases: Schema

Schema for PATCH /shop_user.

shop_notifications_disabled class-attribute instance-attribute
shop_notifications_disabled = Bool(
    required=False, allow_none=True
)

shop_user_blp module-attribute

shop_user_blp = CustomBlueprint(
    "shop_user", __name__, url_prefix="/api/shop/shop_user"
)

sku

SkuCheckPostJsonSchema

Bases: Schema

Schema for POST /sku/check.

pairs class-attribute instance-attribute
pairs = List(
    Nested(_SkuCheckPairSchema),
    required=True,
    validate=Length(min=1),
    metadata={
        "description": "Warehouse / SKU pairs to verify."
    },
)

sku_blp module-attribute

sku_blp = SaleorExtensionBlueprint(
    "shop_sku",
    __name__,
    url_prefix="/api/shop-saleor-extension/sku",
    auth_context_providers=[
        SaleorAppBridgeAuthContextProvider()
    ],
)

stripe_webhook

shop_stripe_webhooks_blp module-attribute

shop_stripe_webhooks_blp = CustomBlueprint(
    "shop_stripe_webhooks",
    __name__,
    url_prefix="/api/shop/stripe_webhooks",
    auth_context_providers=[AnonymousAuthContextProvider()],
)

testimonial

testimonial_blp module-attribute

testimonial_blp = CustomBlueprint(
    "shop_testimonial", __name__, url_prefix="/api/shop"
)

components.shop.public.enums

ContactLensAddition

Bases: AlanBaseEnum

This enum is used to store the addition of a contact lens.

high class-attribute instance-attribute

high = 'high'

low class-attribute instance-attribute

low = 'low'

medium class-attribute instance-attribute

medium = 'medium'

ContactLensItemType

Bases: AlanBaseEnum

This enum is used to store the type of a contact lens item.

left_eye class-attribute instance-attribute

left_eye = 'left_eye'

right_eye class-attribute instance-attribute

right_eye = 'right_eye'

solution class-attribute instance-attribute

solution = 'solution'

ContactLensND

Bases: AlanBaseEnum

This enum is used to store the ND of a contact lens.

D class-attribute instance-attribute

D = 'D'

N class-attribute instance-attribute

N = 'N'

ContactLensProductType

Bases: AlanBaseEnum

This enum is used to store the product type of a contact lens.

contact_lens class-attribute instance-attribute

contact_lens = 'contact_lens'

contact_lens_bi_monthly class-attribute instance-attribute

contact_lens_bi_monthly = 'contact_lens_bi_monthly'

contact_lens_bifocal class-attribute instance-attribute

contact_lens_bifocal = 'contact_lens_bifocal'

contact_lens_colored class-attribute instance-attribute

contact_lens_colored = 'contact_lens_colored'

contact_lens_daily class-attribute instance-attribute

contact_lens_daily = 'contact_lens_daily'

contact_lens_extended_wear class-attribute instance-attribute

contact_lens_extended_wear = 'contact_lens_extended_wear'

contact_lens_for_sport class-attribute instance-attribute

contact_lens_for_sport = 'contact_lens_for_sport'

contact_lens_monthly class-attribute instance-attribute

contact_lens_monthly = 'contact_lens_monthly'

contact_lens_multifocal class-attribute instance-attribute

contact_lens_multifocal = 'contact_lens_multifocal'

contact_lens_progressive class-attribute instance-attribute

contact_lens_progressive = 'contact_lens_progressive'

contact_lens_spherical class-attribute instance-attribute

contact_lens_spherical = 'contact_lens_spherical'

contact_lens_toric class-attribute instance-attribute

contact_lens_toric = 'contact_lens_toric'

ContactLensSolutionType

Bases: AlanBaseEnum

This enum is used to store the solution type of a contact lens.

solution class-attribute instance-attribute

solution = 'solution'

solution_for_rigid class-attribute instance-attribute

solution_for_rigid = 'solution_for_rigid'

solution_for_soft class-attribute instance-attribute

solution_for_soft = 'solution_for_soft'

solution_ophtalmic class-attribute instance-attribute

solution_ophtalmic = 'solution_ophtalmic'

solution_travel_kit class-attribute instance-attribute

solution_travel_kit = 'solution_travel_kit'

ContactLensStatus

Bases: AlanBaseEnum

This enum is used to store the status of a contact lens.

available class-attribute instance-attribute

available = 'available'

not_available class-attribute instance-attribute

not_available = 'not_available'

out_of_stock class-attribute instance-attribute

out_of_stock = 'out_of_stock'

InvoicingDocumentType

Bases: AlanBaseEnum

This enum is used to store the type of invoice generation.

credit_note class-attribute instance-attribute

credit_note = 'credit_note'

invoice_update class-attribute instance-attribute

invoice_update = 'invoice_update'

regular_invoice class-attribute instance-attribute

regular_invoice = 'regular_invoice'

PrescriptionCheckProblemSeverity

Bases: AlanBaseEnum

This enum is used to store the severity of a prescription check problem.

high class-attribute instance-attribute

high = 'high'

low class-attribute instance-attribute

low = 'low'

PrescriptionCheckProblemType

Bases: AlanBaseEnum

This enum is used to store the type of a prescription check problem.

beneficiary class-attribute instance-attribute

beneficiary = 'beneficiary'

brand class-attribute instance-attribute

brand = 'brand'

date class-attribute instance-attribute

date = 'date'

left_addition class-attribute instance-attribute

left_addition = 'left-addition'

left_axis class-attribute instance-attribute

left_axis = 'left-axis'

left_curve class-attribute instance-attribute

left_curve = 'left-curve'

left_cylinder class-attribute instance-attribute

left_cylinder = 'left-cylinder'

left_sphere class-attribute instance-attribute

left_sphere = 'left-sphere'

right_addition class-attribute instance-attribute

right_addition = 'right-addition'

right_axis class-attribute instance-attribute

right_axis = 'right-axis'

right_curve class-attribute instance-attribute

right_curve = 'right-curve'

right_cylinder class-attribute instance-attribute

right_cylinder = 'right-cylinder'

right_sphere class-attribute instance-attribute

right_sphere = 'right-sphere'

trial_lenses class-attribute instance-attribute

trial_lenses = 'trial_lenses'

type class-attribute instance-attribute

type = 'type'

SkuCheckStatus

Bases: AlanBaseEnum

Outcome of a per-(warehouse, SKU) existence check against the supplier's source-of-truth.

error covers transient supplier failures captured per-pair so a single broken SKU does not fail the whole batch — the underlying exception is logged server-side. not_applicable is returned when the supplier exists but has no Shopify processor (e.g. Sveltus, Nutri&Co) and is therefore covered by the daily catalog sync instead. unknown_warehouse is returned when the warehouse slug does not resolve to any registered supplier (typo, decommissioned warehouse, or a slug Ops typed manually).

error class-attribute instance-attribute

error = 'error'

not_applicable class-attribute instance-attribute

not_applicable = 'not_applicable'

not_found class-attribute instance-attribute

not_found = 'not_found'

unknown_warehouse class-attribute instance-attribute

unknown_warehouse = 'unknown_warehouse'

valid class-attribute instance-attribute

valid = 'valid'

TransactionType

Bases: AlanBaseEnum

This enum is used to store the type of a transaction.

claim class-attribute instance-attribute

claim = 'claim'

stripe class-attribute instance-attribute

stripe = 'stripe'

UsageSource

Bases: AlanBaseEnum

This enum is used to store the source of an claim usage.

backend class-attribute instance-attribute

backend = 'backend'

saleor class-attribute instance-attribute

saleor = 'saleor'

UsageStatus

Bases: AlanBaseEnum

This enum is used to store the status of a claim usage.

pending class-attribute instance-attribute

pending = 'pending'

processed class-attribute instance-attribute

processed = 'processed'

refunded class-attribute instance-attribute

refunded = 'refunded'

UsageType

Bases: AlanBaseEnum

This enum is used to store the type of claim usage.

contact_lens class-attribute instance-attribute

contact_lens = 'contact_lens'

glasses class-attribute instance-attribute

glasses = 'glasses'

other class-attribute instance-attribute

other = 'other'

ValidationDecision

Bases: AlanBaseEnum

This enum is used to store the decision of a validation.

approved class-attribute instance-attribute

approved = 'approved'

rejected class-attribute instance-attribute

rejected = 'rejected'

skipped class-attribute instance-attribute

skipped = 'skipped'

components.shop.public.events

ShopTestimonialPublished dataclass

ShopTestimonialPublished(
    testimonial,
    rating,
    product_id,
    user_name,
    app_user_id,
    app_id,
    external_order_id,
)

Bases: WebhookMessage

Event triggered when a ShopTestimonial has been published.

app_id instance-attribute

app_id

app_user_id instance-attribute

app_user_id

external_order_id instance-attribute

external_order_id

product_id instance-attribute

product_id

rating instance-attribute

rating

testimonial instance-attribute

testimonial

user_name instance-attribute

user_name

components.shop.public.flask_admin_configuration

components.shop.public.globalization

base_prescription_checker

BasePrescriptionChecker

BasePrescriptionChecker(saleor_client=None, dry_run=False)

Bases: ABC

Base class for prescription checkers, providing methods to implement the prescription checking logic.

Source code in components/shop/public/globalization/base_prescription_checker.py
def __init__(
    self, saleor_client: Optional[SaleorClient] = None, dry_run: bool = False
):
    self.saleor_client = saleor_client or get_saleor_client()
    self.dry_run = dry_run
check_contact_lens_prescription_content abstractmethod
check_contact_lens_prescription_content(
    beneficiary_user_id,
    content,
    contact_lens_items,
    order_date=None,
)

Check the contact lenses prescription content.

Source code in components/shop/public/globalization/base_prescription_checker.py
@abstractmethod
def check_contact_lens_prescription_content(
    self,
    beneficiary_user_id: str,
    content: PrescriptionContent,
    contact_lens_items: list[ContactLensItem],
    order_date: date | None = None,
) -> PrescriptionCheckerResult:
    """Check the contact lenses prescription content."""
check_glasses_prescription_content abstractmethod
check_glasses_prescription_content(
    beneficiary_user_id, beneficiary_calendar_age, content
)

Check the glasses prescription content.

Source code in components/shop/public/globalization/base_prescription_checker.py
@abstractmethod
def check_glasses_prescription_content(
    self,
    beneficiary_user_id: str,
    beneficiary_calendar_age: int,
    content: PrescriptionContent,
) -> PrescriptionCheckerResult:
    """Check the glasses prescription content."""
check_saleor_contact_lens_checkout_prescription_content abstractmethod
check_saleor_contact_lens_checkout_prescription_content(
    checkout_id, content
)

Check the SaleorCheckout contact lens prescription.

Source code in components/shop/public/globalization/base_prescription_checker.py
@abstractmethod
def check_saleor_contact_lens_checkout_prescription_content(
    self,
    checkout_id: str,
    content: PrescriptionContent,
) -> PrescriptionCheckerResult:
    """Check the SaleorCheckout contact lens prescription."""
check_saleor_contact_lens_fulfillment_prescription abstractmethod
check_saleor_contact_lens_fulfillment_prescription(
    order, fulfillment
)

Check the SaleorOrder prescription.

Source code in components/shop/public/globalization/base_prescription_checker.py
@abstractmethod
def check_saleor_contact_lens_fulfillment_prescription(
    self,
    order: SaleorOrder,
    fulfillment: SaleorFulfillment,
) -> Optional[PrescriptionCheckerResult]:
    """Check the SaleorOrder prescription."""
check_saleor_glasses_checkout_prescription_content abstractmethod
check_saleor_glasses_checkout_prescription_content(
    checkout_id, content
)

Check the SaleorCheckout glasses prescription.

Source code in components/shop/public/globalization/base_prescription_checker.py
@abstractmethod
def check_saleor_glasses_checkout_prescription_content(
    self,
    checkout_id: str,
    content: PrescriptionContent,
) -> PrescriptionCheckerResult:
    """Check the SaleorCheckout glasses prescription."""
document_content_is_of_type abstractmethod
document_content_is_of_type(content, document_type)

Abstract method to check if the document content is of the given type.

Source code in components/shop/public/globalization/base_prescription_checker.py
@abstractmethod
def document_content_is_of_type(
    self,
    content: PrescriptionContent,
    document_type: str,
) -> bool:
    """Abstract method to check if the document content is of the given type."""
dry_run class-attribute instance-attribute
dry_run = dry_run
extract_prescription_duration_in_years abstractmethod
extract_prescription_duration_in_years(
    content, duration_type="any"
)

Extract prescription duration in years from transcription.

Source code in components/shop/public/globalization/base_prescription_checker.py
@abstractmethod
def extract_prescription_duration_in_years(
    self,
    content: PrescriptionContent,
    duration_type: Literal["any", "qsp", "validity"] = "any",
) -> int | None:
    """Extract prescription duration in years from transcription."""
retrieve_prescription_content abstractmethod
retrieve_prescription_content(
    prescription_id,
    compute_if_not_parsed_yet=False,
    use_transcription=False,
    automated_parsing_only=False,
)

Abstract method to retrieve the prescription content.

Source code in components/shop/public/globalization/base_prescription_checker.py
@abstractmethod
def retrieve_prescription_content(
    self,
    prescription_id: str,
    compute_if_not_parsed_yet: bool = False,
    use_transcription: bool = False,
    automated_parsing_only: bool = False,
) -> Optional[PrescriptionContent]:
    """Abstract method to retrieve the prescription content."""
retrieve_prescription_transcription abstractmethod
retrieve_prescription_transcription(prescription_id)

Abstract method to retrieve the prescription transcription.

Source code in components/shop/public/globalization/base_prescription_checker.py
@abstractmethod
def retrieve_prescription_transcription(
    self,
    prescription_id: str,
) -> Optional[PrescriptionTranscription]:
    """Abstract method to retrieve the prescription transcription."""
saleor_client instance-attribute
saleor_client = saleor_client or get_saleor_client()

base_shop_adapter

BaseShopAdapter

Bases: ABC

Base class for shop adapters, providing methods to implement the Shop.

create_insurance_document_from_parsed_partner_invoice abstractmethod
create_insurance_document_from_parsed_partner_invoice(
    order, fulfillment, parsed_invoice
)

Persist the country-specific claim representation for an uploaded partner invoice.

Receives the parsed invoice returned by the shop-level parse_and_validate_partner_invoice. Returns the new insurance document id.

Source code in components/shop/public/globalization/base_shop_adapter.py
@abstractmethod
def create_insurance_document_from_parsed_partner_invoice(
    self,
    order: SaleorOrder,
    fulfillment: SaleorFulfillment,
    parsed_invoice: ParsedPartnerInvoice,
) -> int:
    """Persist the country-specific claim representation for an uploaded
    partner invoice.

    Receives the parsed invoice returned by the shop-level
    ``parse_and_validate_partner_invoice``. Returns the new insurance
    document id.
    """
generate_contact_lens_global_invoice abstractmethod
generate_contact_lens_global_invoice(
    transactions_to_invoice, period, is_preview=False
)

Generate the invoice from Alan Tech to Alan Insurance for the contact lens claim transactions of a given period.

Source code in components/shop/public/globalization/base_shop_adapter.py
@abstractmethod
def generate_contact_lens_global_invoice(
    self,
    transactions_to_invoice: list[SaleorTransactionToInvoice],
    period: date,
    is_preview: bool = False,
) -> str:
    """Generate the invoice from Alan Tech to Alan Insurance for the contact lens claim transactions of a given period."""
generate_credit_note abstractmethod
generate_credit_note(order)

Generate a credit note for an order.

Source code in components/shop/public/globalization/base_shop_adapter.py
@abstractmethod
def generate_credit_note(
    self,
    order: SaleorOrder,
) -> OrderInvoice:
    """Generate a credit note for an order."""
generate_order_invoice abstractmethod
generate_order_invoice(
    order,
    is_preview=False,
    invoice_type=InvoicingDocumentType.regular_invoice,
)

Generate an invoice for an order.

Source code in components/shop/public/globalization/base_shop_adapter.py
@abstractmethod
def generate_order_invoice(
    self,
    order: SaleorOrder,
    is_preview: bool = False,
    invoice_type: InvoicingDocumentType = InvoicingDocumentType.regular_invoice,
) -> OrderInvoice:
    """Generate an invoice for an order."""
get_contact_lens_coverage_definition abstractmethod
get_contact_lens_coverage_definition(user_id)

Return contact lens coverage definition, abstracted from country-specific guarantee codes.

Source code in components/shop/public/globalization/base_shop_adapter.py
@abstractmethod
def get_contact_lens_coverage_definition(self, user_id: str) -> ContactLensCoverage:
    """Return contact lens coverage definition, abstracted from country-specific guarantee codes."""
get_contact_lens_global_invoice_url abstractmethod
get_contact_lens_global_invoice_url(period)

Get the URL of the contact lens global invoice.

Source code in components/shop/public/globalization/base_shop_adapter.py
@abstractmethod
def get_contact_lens_global_invoice_url(
    self,
    period: date,
) -> str | None:
    """Get the URL of the contact lens global invoice."""
get_coverage_usage_for_user abstractmethod
get_coverage_usage_for_user(
    user_id, usage_type, reference_date=None
)

Return the remaining usage for a user.

Source code in components/shop/public/globalization/base_shop_adapter.py
@abstractmethod
def get_coverage_usage_for_user(
    self,
    user_id: str,
    usage_type: UsageType,
    reference_date: date | None = None,
) -> CoverageUsage:
    """Return the remaining usage for a user."""
get_glasses_coverage_definition abstractmethod
get_glasses_coverage_definition(user_id)

Return glasses coverage definition, abstracted from country-specific guarantee codes.

Source code in components/shop/public/globalization/base_shop_adapter.py
@abstractmethod
def get_glasses_coverage_definition(self, user_id: str) -> GlassesCoverage:
    """Return glasses coverage definition, abstracted from country-specific guarantee codes."""
get_marmot_profile_url abstractmethod
get_marmot_profile_url(user_id)

Get a user Marmot profile URL by their ID.

Source code in components/shop/public/globalization/base_shop_adapter.py
@abstractmethod
def get_marmot_profile_url(self, user_id: str) -> str | None:
    """Get a user Marmot profile URL by their ID."""
get_order_beneficiary_info abstractmethod
get_order_beneficiary_info(user_id)

Return the beneficiary info (name, SSN, gender, birth date) needed by an optician to issue an invoice for a shop order.

Source code in components/shop/public/globalization/base_shop_adapter.py
@abstractmethod
def get_order_beneficiary_info(self, user_id: str) -> OrderBeneficiaryInfo:
    """Return the beneficiary info (name, SSN, gender, birth date) needed by an optician to issue an invoice for a shop order."""
get_order_invoice_url abstractmethod
get_order_invoice_url(invoice_id)

Get the URL of the order invoice.

Source code in components/shop/public/globalization/base_shop_adapter.py
@abstractmethod
def get_order_invoice_url(
    self,
    invoice_id: str,
) -> str:
    """Get the URL of the order invoice."""
    return f"{current_config['FRONT_END_BASE_URL']}/marmot/document/{invoice_id}"
get_pending_claim_usages_for_user abstractmethod
get_pending_claim_usages_for_user(user_id, usage_type)

Return the uninvoiced orders for a user.

return SaleorHelper().get_pending_claim_usages_for_user( app_id=AppName.ALAN_FR, user_id=user_id, usage_type=usage_type, )

Source code in components/shop/public/globalization/base_shop_adapter.py
@abstractmethod
def get_pending_claim_usages_for_user(
    self,
    user_id: str,
    usage_type: UsageType,
) -> list[PendingClaimUsage]:
    """
    Return the uninvoiced orders for a user.

    return SaleorHelper().get_pending_claim_usages_for_user(
        app_id=AppName.ALAN_FR,
        user_id=user_id,
        usage_type=usage_type,
    )
    """
get_prescription_checker abstractmethod
get_prescription_checker(saleor_client, dry_run=False)

Return a prescription checker class.

Source code in components/shop/public/globalization/base_shop_adapter.py
@abstractmethod
def get_prescription_checker(
    self, saleor_client: SaleorClient, dry_run: bool = False
) -> BasePrescriptionChecker:
    """Return a prescription checker class."""
has_pending_shop_order abstractmethod
has_pending_shop_order(prescription_id)

Return True if the prescription is linked to at least one pending shop order.

Source code in components/shop/public/globalization/base_shop_adapter.py
@abstractmethod
def has_pending_shop_order(
    self,
    prescription_id: str,
) -> bool:
    """Return True if the prescription is linked to at least one pending shop order."""
resolve_user_name_from_insurance_profile_id abstractmethod
resolve_user_name_from_insurance_profile_id(profile_id)

Resolve a user's full name from their insurance profile ID.

Source code in components/shop/public/globalization/base_shop_adapter.py
@abstractmethod
def resolve_user_name_from_insurance_profile_id(
    self, profile_id: str
) -> str | None:
    """Resolve a user's full name from their insurance profile ID."""
resolve_user_name_from_user_id abstractmethod
resolve_user_name_from_user_id(user_id)

Resolve a user's first and last name from their user ID.

Source code in components/shop/public/globalization/base_shop_adapter.py
@abstractmethod
def resolve_user_name_from_user_id(
    self, user_id: str
) -> tuple[str | None, str | None]:
    """Resolve a user's first and last name from their user ID."""
tombstone_insurance_document abstractmethod
tombstone_insurance_document(
    insurance_document_id, force=False
)

Tombstone (soft delete) an insurance document.

Source code in components/shop/public/globalization/base_shop_adapter.py
@abstractmethod
def tombstone_insurance_document(
    self,
    insurance_document_id: str,
    force: bool = False,
) -> None:
    """Tombstone (soft delete) an insurance document."""
upload_invoicing_document abstractmethod
upload_invoicing_document(
    order, invoicing_document, is_credit_note=False
)

Upload invoicing document.

Source code in components/shop/public/globalization/base_shop_adapter.py
@abstractmethod
def upload_invoicing_document(
    self,
    order: SaleorOrder,
    invoicing_document: OrderInvoice,
    is_credit_note: bool = False,
) -> str | None:
    """Upload invoicing document."""
upload_order_invoice abstractmethod
upload_order_invoice(order, invoice)

Upload order invoice and create the corresponding care acts.

Source code in components/shop/public/globalization/base_shop_adapter.py
@abstractmethod
def upload_order_invoice(
    self,
    order: SaleorOrder,
    invoice: OrderInvoice,
) -> str | None:
    """Upload order invoice and create the corresponding care acts."""

shop_adapter

get_app_specific_shop_adapter

get_app_specific_shop_adapter(app_id)

Return an app-specific shop adapter based on the app ID or raise an error if not found.

Source code in components/shop/public/globalization/shop_adapter.py
def get_app_specific_shop_adapter(app_id: str) -> BaseShopAdapter:
    """Return an app-specific shop adapter based on the app ID or raise an error if not found."""
    shop_adapter = get_app_specific_shop_adapter_or_none(app_id=app_id)
    if not shop_adapter:
        current_logger.error(
            f"Shop: [globalization] No shop adapter found for app_id: {app_id}"
        )
        raise ValueError(f"Shop adapter not found: {app_id}")
    return shop_adapter

get_app_specific_shop_adapter_or_none

get_app_specific_shop_adapter_or_none(app_id)

Return an app-specific shop adapter based on the app ID.

Source code in components/shop/public/globalization/shop_adapter.py
def get_app_specific_shop_adapter_or_none(app_id: str) -> Optional[BaseShopAdapter]:
    """Return an app-specific shop adapter based on the app ID."""
    match app_id:
        case AppName.ALAN_FR:
            from components.fr.internal.shop.public.shop_adapter import (  # noqa: ALN039
                ShopAdapter as ShopAdapterFr,
            )

            return ShopAdapterFr()

    return None

components.shop.public.services

discounts

create_shop_discount

create_shop_discount(code, amount, expires_at, title)

Create a discount to be used in the shop During the transition period, we create both a shopify discount code and a saleor voucher So the member will be able to find the discount in both places

Source code in components/shop/public/services/discounts.py
def create_shop_discount(
    code: str, amount: VoucherDataAmount, expires_at: datetime, title: str
) -> None:
    """
    Create a discount to be used in the shop
    During the transition period, we create both a shopify discount code and a saleor voucher
    So the member will be able to find the discount in both places
    """
    from components.shop.internal.business_logic.discounts.create_discount import (
        create_discount,
    )

    return create_discount(
        code=code,
        amount=amount,
        expires_at=expires_at,
        title=title,
    )

get_processed_orders_with_discounts

get_processed_orders_with_discounts(after)

Get orders with discounts processed after date from shopify

Source code in components/shop/public/services/discounts.py
def get_processed_orders_with_discounts(
    after: date,
) -> list[ProcessedOrderAndDiscounts]:
    """
    Get orders with discounts processed after date from shopify
    """
    from components.shop.internal.business_logic.discounts.get_processed_orders_with_discounts import (
        get_processed_orders_with_discounts,
    )

    return get_processed_orders_with_discounts(after=after)

products

AvailableShopProductTypes

Bases: str, Enum

Duplicated from clinic — shop can't import clinic.

CONTACT_LENSES class-attribute instance-attribute
CONTACT_LENSES = 'contact_lenses'

PRODUCT_TYPES_BY_SPECIALTY module-attribute

PRODUCT_TYPES_BY_SPECIALTY = {'optician': [CONTACT_LENSES]}

ProductData dataclass

ProductData(id, name, brand, image_url=None)

Product data exposed by the shop public API.

brand instance-attribute
brand
id instance-attribute
id
image_url class-attribute instance-attribute
image_url = None
name instance-attribute
name

get_product_types_for_specialty

get_product_types_for_specialty(specialty)

Return the shop product types a given medical specialty can recommend.

Source code in components/shop/public/services/products.py
def get_product_types_for_specialty(
    specialty: str,
) -> list[AvailableShopProductTypes]:
    """Return the shop product types a given medical specialty can recommend."""
    return PRODUCT_TYPES_BY_SPECIALTY.get(specialty, [])

get_shop_products

get_shop_products(shop_product_types)

Fetch shop products from the FR Saleor channel.

Source code in components/shop/public/services/products.py
@cached_for(minutes=5)
def get_shop_products(
    shop_product_types: list[AvailableShopProductTypes],
) -> list[ProductData]:
    """Fetch shop products from the FR Saleor channel."""
    from components.shop.internal.saleor.codegen.client.saleor_client import (
        get_saleor_client,
    )
    from components.shop.internal.saleor.consts import CHANNEL_ALAN_FR_SLUG
    from components.shop.internal.saleor.generated.input_types import (
        ProductFilterInput,
    )

    client = get_saleor_client()

    # Restrict to products a member can actually find, open and buy
    customer_visible_filter = ProductFilterInput(
        isPublished=True, isAvailable=True, isVisibleInListing=True
    )

    saleor_products = []

    if AvailableShopProductTypes.CONTACT_LENSES in shop_product_types:
        saleor_products += client.fetch_all(
            client.get_contact_lens_products,  # pyrefly: ignore [bad-argument-type]
            channel=CHANNEL_ALAN_FR_SLUG,
            filters=customer_visible_filter,
        )

    return [
        ProductData(
            id=product.id,
            name=product.name,
            brand=product.brand,
            image_url=media.url if (media := first_of_list(product.medias)) else None,
        )
        for product in saleor_products
    ]