shop.modules ============ .. py:module:: shop.modules Submodules ---------- .. toctree:: :maxdepth: 1 /viur/shop/modules/abstract/index /viur/shop/modules/address/index /viur/shop/modules/api/index /viur/shop/modules/cart/index /viur/shop/modules/discount/index /viur/shop/modules/discount_condition/index /viur/shop/modules/order/index /viur/shop/modules/shipping/index /viur/shop/modules/shipping_config/index /viur/shop/modules/vat_rate/index Classes ------- .. autoapisummary:: shop.modules.Address shop.modules.Api shop.modules.Cart shop.modules.Discount shop.modules.DiscountCondition shop.modules.Order shop.modules.Shipping shop.modules.ShippingConfig shop.modules.VatRate Package Contents ---------------- .. py:class:: Address(moduleName = None, modulePath = None, shop = None, *args, **kwargs) Bases: :py:obj:`shop.modules.abstract.ShopModuleAbstract`, :py:obj:`viur.core.prototypes.List` Abstract Class for all viur-shop sub/nested modules. The implementations should set `moduleName` as class variable, so the final module name for routing it not affected by the name of custom classes. .. py:attribute:: moduleName :value: 'address' .. py:attribute:: kindName :value: '{{viur_shop_modulename}}_address' .. py:attribute:: reference_user_created_skeletons_in_session :value: True If True, keys of skeletons that the current user has created will be stored in the session. .. py:attribute:: default_order :type: viur.core.prototypes.skelmodule.DEFAULT_ORDER_TYPE .. py:method:: adminInfo() .. py:method:: canAdd() .. py:method:: canEdit(skel) .. py:method:: listFilter(query) .. py:method:: onAdded(skel) .. py:method:: onEdited(skel) .. py:method:: _disable_old_default(skel) Disable old is_default .. py:method:: clone_address(key) .. py:class:: Api(moduleName = None, modulePath = None, shop = None, *args, **kwargs) Bases: :py:obj:`viur.shop.modules.abstract.ShopModuleAbstract` Abstract Class for all viur-shop sub/nested modules. The implementations should set `moduleName` as class variable, so the final module name for routing it not affected by the name of custom classes. .. py:property:: json_renderer :type: viur.core.render.json.default.DefaultRender .. py:method:: article_view(article_key, parent_cart_key) View an article in the cart .. py:method:: article_add(*, article_key, quantity = 1, quantity_mode = QuantityMode.REPLACE, parent_cart_key = SENTINEL, **kwargs) Add an article to the cart :param article_key: Key of the article to add. :param quantity: Quantity of the article to add. :param quantity_mode: Behavior of the quantity: absolute or relative valuation :param parent_cart_key: Key of the (sub) cart (node) to which this leaf will be added as a child. Use "BASKET" as key to use the basket of the current session. .. py:method:: article_update(*, article_key, quantity, quantity_mode = QuantityMode.REPLACE, parent_cart_key = SENTINEL, **kwargs) Update an existing article in the cart :param article_key: Key of the article to update. Note: This is not the key of the leaf skel! :param quantity: Quantity of the article to update. :param quantity_mode: Behavior of the quantity: absolute or relative valuation :param parent_cart_key: Optional. Key of the (sub) cart (node) to which this leaf will be moved to as a child. Use "BASKET" as key to use the basket of the current session. .. py:method:: article_remove(*, article_key, parent_cart_key, **kwargs) Remove an article from the cart .. py:method:: article_move(*, article_key, parent_cart_key, new_parent_cart_key) Move an article inside a cart .. py:method:: cart_add(*, parent_cart_key = None, cart_type = SENTINEL, name = SENTINEL, customer_comment = SENTINEL, shipping_address_key = SENTINEL, shipping_key = SENTINEL, discount_key = SENTINEL, **kwargs) Add a new cart node :param parent_cart_key: Key of the parent cart :param cart_type: Type of the cart node, see :class:`CartType` :param name: Optional. Name of the cart node :param customer_comment: Optional. Comment to this node, by customer. :param shipping_address_key: Optional. Key of the address :param shipping_key: Optional. Key of the shipping :param discount_key: Optional. Key of the discount Returns: The created cart node skel .. py:method:: cart_update(*, cart_key, cart_type = SENTINEL, name = SENTINEL, customer_comment = SENTINEL, shipping_address_key = SENTINEL, shipping_key = SENTINEL, discount_key = SENTINEL, **kwargs) Update an existing cart node :param cart_key: Key of the cart node to be updated :param cart_type: Type of the cart node, see :class:`CartType` :param name: Optional. Name of the cart node :param customer_comment: Optional. Comment to this node, by customer. :param shipping_address_key: Optional. Key of the address :param shipping_key: Optional. Key of the shipping :param discount_key: Optional. Key of the discount Returns: The updated cart node skel .. py:method:: cart_remove(*, cart_key) Remove a cart node. Removes itself and all children :param cart_key: Key of the cart node to be removed .. py:method:: cart_clear(*, cart_key = SENTINEL, keep_sub_carts = False) Remove direct or all children :param cart_key: Key of the (sub) cart (node) from which the children should be removed. Use "BASKET" as key to use the basket of the current session. :param keep_sub_carts: Keep child nodes, remove only leafs .. py:method:: basket_list() List the children of the basket (the cart stored in the session) :raises errors.PreconditionFailed: If no basket created yet for this session .. py:method:: basket_view(*, create_if_missing = False) View the basket (the cart stored in the session) itself :param create_if_missing: Create the basket if not already created for this session :raises errors.PreconditionFailed: If no basket created yet for this session (and it should not be created) See also :meth:`basket_view` to view any cart. .. py:method:: cart_list(cart_key = None) List root nodes or children of a cart If a cart key is provided, the direct children (nodes and leafs) will be returned. Otherwise (without a key), the root nodes will be returned. :param cart_key: list direct children (nodes and leafs) of this parent node .. py:method:: cart_view(cart_key) View a cart itself See also :meth:`basket_view` to view the current basket. .. py:method:: order_add(*, cart_key, payment_provider = SENTINEL, billing_address_key = SENTINEL, customer_key = SENTINEL, state_ordered = SENTINEL, state_paid = SENTINEL, state_rts = SENTINEL, **kwargs) .. py:method:: order_update(*, order_key, payment_provider = SENTINEL, billing_address_key = SENTINEL, customer_key = SENTINEL, state_ordered = SENTINEL, state_paid = SENTINEL, state_rts = SENTINEL, **kwargs) .. py:method:: order_remove(*, order_key) .. py:method:: order_list(**kwargs) List the orders of the current user .. py:method:: order_view(order_key = 'SESSION') View an order :param order_key: Key of the order to view. Use "SESSION" as key to view the order of the current session .. py:method:: _get_order_view_result(order_skel) .. py:method:: discount_add(*, code = None, discount_key = None) parameter code xor discount_key: str | db.Key Sucht nach Rabatt mit dem code xor key, je nach Typ (Artikel/Warenkorb) suche ...ende parent_node oder erzeuge eine und setze dort die discount Relation. .. py:method:: discount_remove(*, discount_key) .. py:method:: shipping_list(cart_key) Lists available shipping options for a (sub)cart :param cart_key: Key of the parent cart :returns: list of :class:`ShippingSkel` `SkeletonInstance`s .. py:method:: _normalize_external_key(external_key, parameter_name, can_be_None = False) Convert urlsafe key to db.Key and raise an error on invalid in key. .. py:class:: Cart(moduleName = None, modulePath = None, shop = None, *args, **kwargs) Bases: :py:obj:`viur.shop.modules.abstract.ShopModuleAbstract`, :py:obj:`viur.core.prototypes.Tree` Abstract Class for all viur-shop sub/nested modules. The implementations should set `moduleName` as class variable, so the final module name for routing it not affected by the name of custom classes. .. py:attribute:: moduleName :value: 'cart' .. py:attribute:: nodeSkelCls .. py:attribute:: leafSkelCls .. py:method:: adminInfo() .. py:method:: baseSkel(skelType, sub_skel = None, *args, **kwargs) Extend default baseSkel() by sub_skel parameter .. py:method:: canView(skelType, skel) .. py:property:: current_session_cart_key :type: viur.core.db.Key | None .. py:method:: get_current_session_cart_key(*, create_if_missing = False) .. py:property:: current_session_cart :type: viur.shop.types.SkeletonInstance_T[shop.skeletons.cart.CartNodeSkel] .. py:method:: _ensure_current_session_cart() .. py:method:: detach_session_cart() .. py:method:: _set_basket_txn(user_key, basket_key) :staticmethod: .. py:method:: get_available_root_nodes(*args, **kwargs) .. py:attribute:: getAvailableRootNodes .. py:method:: listRootNodes(*args, **kwargs) Renders a list of all available repositories for the current user using the modules default renderer. :returns: The rendered representation of the available root-nodes. .. py:method:: is_valid_node(node_key, root_node = False) is this a valid node key for the user? :param node_key: Key of node to check :param root_node: Must this be a root node, or is any node okay? .. py:method:: get_children(parent_cart_key, **filters) .. py:method:: get_children_from_cache(parent_cart_key) .. py:method:: clear_children_cache() .. py:method:: cart_get(cart_key, skel_type) .. py:method:: get_article(article_key, parent_cart_key, *, must_be_listed = True) .. py:method:: add_or_update_article(article_key, parent_cart_key, *, quantity, quantity_mode, **kwargs) .. py:method:: copy_article_values(article_skel, skel) Copy values from the article to the cart leaf .. py:method:: move_article(article_key, parent_cart_key, new_parent_cart_key) .. py:method:: cart_add(*, parent_cart_key = None, cart_type = None, name = SENTINEL, customer_comment = SENTINEL, shipping_address_key = SENTINEL, shipping_key = SENTINEL, discount_key = SENTINEL, **kwargs) .. py:method:: cart_update(cart_key, *, parent_cart_key = SENTINEL, cart_type = None, name = SENTINEL, customer_comment = SENTINEL, shipping_address_key = SENTINEL, shipping_key = SENTINEL, discount_key = SENTINEL, **kwargs) .. py:method:: _cart_set_values(skel, *, parent_cart_key = SENTINEL, cart_type = None, name = SENTINEL, customer_comment = SENTINEL, shipping_address_key = SENTINEL, shipping_key = SENTINEL, discount_key = SENTINEL) .. py:method:: cart_remove(cart_key) .. py:method:: cart_clear(cart_key, *, keep_sub_carts = False) .. py:method:: additional_add_or_update_article(skel, /, **kwargs) Hook method called by :meth:`add_or_update_article` before the skeleton is saved. This method can be overridden in a subclass to implement additional API fields or make further modifications to the cart skeleton (`skel`). By default, it raises an exception if unexpected arguments (``kwargs``) are provided and returns the unchanged `skel` object. :param skel: The current instance of the cart item skeleton. :param kwargs: Additional optional arguments for extended implementations. :raises TooManyArgumentsException: If unexpected arguments are passed in ``kwargs``. :return: The (potentially modified) cart item skeleton. .. py:method:: additional_cart_add(skel, /, **kwargs) Hook method called by :meth:`cart_add` before the skeleton is saved. This method can be overridden in a subclass to implement additional API fields or make further modifications to the cart skeleton (`skel`). By default, it raises an exception if unexpected arguments (``kwargs``) are provided and returns the unchanged `skel` object. :param skel: The current instance of the cart skeleton. :param kwargs: Additional optional arguments for extended implementations. :raises TooManyArgumentsException: If unexpected arguments are passed in ``kwargs``. :return: The (potentially modified) cart skeleton. .. py:method:: additional_cart_update(skel, /, **kwargs) Hook method called by :meth:`cart_update` before the skeleton is saved. This method can be overridden in a subclass to implement additional API fields or make further modifications to the cart skeleton (`skel`). By default, it raises an exception if unexpected arguments (``kwargs``) are provided and returns the unchanged `skel` object. :param skel: The current instance of the cart skeleton. :param kwargs: Additional optional arguments for extended implementations. :raises TooManyArgumentsException: If unexpected arguments are passed in ``kwargs``. :return: The (potentially modified) cart skeleton. .. py:method:: freeze_cart(cart_key) Freeze (lock) cart values and children items. :param cart_key: Key of the (sub-)cart skeleton. :return: The frozen CartNode skeleton. .. py:method:: freeze_leaf(leaf_skel) .. py:method:: get_discount_for_leaf(leaf_key_or_skel) .. py:method:: add_new_parent(leaf_skel, **kwargs) .. py:method:: get_cached_cart_skel(key) .. py:method:: get_closest_node(start, condition = lambda skel: True) .. py:class:: Discount(moduleName = None, modulePath = None, shop = None, *args, **kwargs) Bases: :py:obj:`shop.modules.abstract.ShopModuleAbstract`, :py:obj:`viur.core.prototypes.List` Abstract Class for all viur-shop sub/nested modules. The implementations should set `moduleName` as class variable, so the final module name for routing it not affected by the name of custom classes. .. py:attribute:: moduleName :value: 'discount' .. py:attribute:: kindName :value: '{{viur_shop_modulename}}_discount' .. py:method:: adminInfo() .. py:method:: search(code = None, discount_key = None) .. py:method:: apply(code = None, discount_key = None) .. py:method:: can_apply(skel, *, cart_key = None, article_skel = None, code = None, context = DiscountValidationContext.NORMAL) .. py:property:: current_automatically_discounts :type: list[viur.shop.types.SkeletonInstance_T[shop.skeletons.DiscountSkel]] .. py:method:: remove(discount_key) .. py:class:: DiscountCondition Bases: :py:obj:`shop.modules.abstract.ShopModuleAbstract`, :py:obj:`viur.core.prototypes.List` .. py:attribute:: moduleName :value: 'discount_condition' .. py:attribute:: kindName :value: '{{viur_shop_modulename}}_discount_condition' .. py:method:: adminInfo() .. py:method:: canEdit(skel) .. py:method:: editSkel(*args, **kwargs) .. py:method:: onAdd(skel) .. py:method:: onEdit(skel) .. py:method:: onClone(skel, src_skel) .. py:method:: onAdded(skel) .. py:method:: onEdited(skel) .. py:method:: onCloned(skel, src_skel) .. py:method:: on_change(skel, event) .. py:method:: on_changed(skel, event) .. py:method:: generate_subcodes(parent_key, prefix, amount) Generate subcodes for a parent individual code. .. py:method:: get_skel(key) :classmethod: .. py:method:: get_by_code(code = None) .. py:method:: get_discounts_from_cart(cart_key) .. py:method:: mark_discount_used(order_skel, payment, *args, **kwargs) :staticmethod: Increase quantity_used on discount of an ordered cart .. py:class:: Order(moduleName = None, modulePath = None, shop = None, *args, **kwargs) Bases: :py:obj:`shop.modules.abstract.ShopModuleAbstract`, :py:obj:`viur.core.prototypes.List` Abstract Class for all viur-shop sub/nested modules. The implementations should set `moduleName` as class variable, so the final module name for routing it not affected by the name of custom classes. .. py:attribute:: moduleName :value: 'order' .. py:attribute:: kindName :value: '{{viur_shop_modulename}}_order' .. py:attribute:: reference_user_created_skeletons_in_session :value: True If True, keys of skeletons that the current user has created will be stored in the session. .. py:method:: adminInfo() .. py:property:: current_session_order_key :type: viur.core.db.Key | None .. py:property:: current_order_skel :type: viur.shop.types.SkeletonInstance_T[shop.skeletons.order.OrderSkel] | None .. py:method:: canView(skel) .. py:method:: payment_providers_list(only_available = True) Get a list of payment providers. This method returns a JSON response containing a dictionary of payment providers. The keys represent provider identifiers, and the values are instances of `PaymentProviderResult` (dict) containing the details of each provider. :param only_available: If ``True`` (default), only payment providers that are currently available will be included in the response. If ``False``, all providers will be listed regardless of availability. :return: A JSON response with a dictionary of payment providers. .. py:method:: get_payment_providers(only_available = True) .. py:method:: order_get(order_key) .. py:method:: order_add(cart_key, payment_provider = SENTINEL, billing_address_key = SENTINEL, customer_key = SENTINEL, state_ordered = SENTINEL, state_paid = SENTINEL, state_rts = SENTINEL, **kwargs) .. py:method:: order_update(order_key, payment_provider = SENTINEL, billing_address_key = SENTINEL, customer_key = SENTINEL, state_ordered = SENTINEL, state_paid = SENTINEL, state_rts = SENTINEL, **kwargs) .. py:method:: _order_set_values(skel, *, payment_provider = SENTINEL, billing_address_key = SENTINEL, customer_key = SENTINEL, state_ordered = SENTINEL, state_paid = SENTINEL, state_rts = SENTINEL) .. py:method:: customer_is_valid(order_skel, customer_key) Checks if the given customer is a valid customer for this skel. The customer must be the same user or an root user. .. py:method:: checkout_start(order_key) Start the checkout process. Requires no errors in :meth:`self.can_checkout`. .. py:method:: can_checkout(order_skel) .. py:method:: freeze_order(order_skel) .. py:method:: _default_assign_uid(order_skel) Default order assign id method. Called as default/fallback for :attr:`Hook.ORDER_ASSIGN_UID`. .. py:method:: checkout_order(order_key) The final order now step. Requires no errors in :meth:`self.can_order`. .. py:method:: can_order(order_skel) .. py:method:: set_checkout_in_progress(order_skel) Set an order to the state *is_checkout_in_progress* .. py:method:: set_ordered(order_skel, payment) Set an order to the state *ordered* .. py:method:: set_paid(order_skel) Set an order to the state *paid* .. py:method:: set_rts(order_skel) Set an order to the state *Ready to ship* .. py:method:: additional_order_add(skel, /, **kwargs) Hook method called by :meth:`order_add` before the skeleton is saved. This method can be overridden in a subclass to implement additional API fields or make further modifications to the order skeleton (`skel`). By default, it raises an exception if unexpected arguments (``kwargs``) are provided and returns the unchanged `skel` object. :param skel: The current instance of the order skeleton. :param kwargs: Additional optional arguments for extended implementations. :raises TooManyArgumentsException: If unexpected arguments are passed in ``kwargs``. :return: The (potentially modified) order skeleton. .. py:method:: additional_order_update(skel, /, **kwargs) Hook method called by :meth:`order_update` before the skeleton is saved. This method can be overridden in a subclass to implement additional API fields or make further modifications to the order skeleton (`skel`). By default, it raises an exception if unexpected arguments (``kwargs``) are provided and returns the unchanged `skel` object. :param skel: The current instance of the order skeleton. :param kwargs: Additional optional arguments for extended implementations. :raises TooManyArgumentsException: If unexpected arguments are passed in ``kwargs``. :return: The (potentially modified) order skeleton. .. py:method:: get_payment_provider_by_name(payment_provider_name) .. py:class:: Shipping Bases: :py:obj:`shop.modules.abstract.ShopModuleAbstract`, :py:obj:`viur.core.prototypes.List` .. py:attribute:: moduleName :value: 'shipping' .. py:attribute:: kindName :value: '{{viur_shop_modulename}}_shipping' .. py:method:: adminInfo() .. py:method:: choose_shipping_skel_for_article(article_skel, *, country = None) Chooses always the cheapest, applicable shipping for an article Ignores the supplier :param country: Ignore the context and get shipping for this country. # TODO(discuss): List all options? .. py:method:: get_shipping_skels_for_cart(*, cart_key = SENTINEL, cart_skel = SENTINEL, country = None, use_cache = False) Get all configured and applicable shippings of all items in the cart # TODO: how do we handle free shipping discounts? :param cart_key: Key of the parent cart node, can be a sub-cart too :param country: Ignore the context and get shipping for this country. :return: A list of :class:`SkeletonInstance`s for the :class:`ShippingSkel`. .. py:class:: ShippingConfig(moduleName = None, modulePath = None, shop = None, *args, **kwargs) Bases: :py:obj:`shop.modules.abstract.ShopModuleAbstract`, :py:obj:`viur.core.prototypes.List` Abstract Class for all viur-shop sub/nested modules. The implementations should set `moduleName` as class variable, so the final module name for routing it not affected by the name of custom classes. .. py:attribute:: moduleName :value: 'shipping_config' .. py:attribute:: kindName :value: '{{viur_shop_modulename}}_shipping_config' .. py:method:: adminInfo() .. py:method:: is_applicable(dest, rel, *, article_skel = None, cart_skel = None, country = None) Check if a shipping configuration is applicable in the current context. Provide eiter `article_skel` for single article context xor `cart_skel` for cart context. :param country: If provided, check if the shipping configuration is applicable for this country. .. py:class:: VatRate Bases: :py:obj:`shop.modules.abstract.ShopModuleAbstract`, :py:obj:`viur.core.prototypes.List` .. py:attribute:: moduleName :value: 'vat_rate' .. py:attribute:: kindName :value: '{{viur_shop_modulename}}_vat_rate' .. py:method:: adminInfo() .. py:method:: vat_rates() Cache the vat rates Since this configuration will not change much, we cache the whole thing persistently within the instance. So after changing the VAT rates, the instance would have to be restarted -- or someone would have to create a pull request with an alternative solution. .. py:method:: _vat_skel() .. py:method:: get_vat_rate_for_country(*, country = None, category) Get the configured vat rate percentage for a country.