shop.modules.cart ================= .. py:module:: shop.modules.cart Attributes ---------- .. autoapisummary:: shop.modules.cart.logger Classes ------- .. autoapisummary:: shop.modules.cart.Cart Functions --------- .. autoapisummary:: shop.modules.cart.delete_guest_cart Module Contents --------------- .. py:data:: logger .. 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() Unlink the current cart from the session (and the user's basket). The cart entity itself is kept. Tolerates a session that has no ``session_cart_key`` (anymore) instead of raising a ``KeyError``. :return: The key of the detached cart, or ``None`` if none was set. .. 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) Remove a cart node with its entire subtree. The subtree is deleted bottom-up and **before** the node itself: leafs first, then sub-nodes, the given node last. This order keeps the tree consistent at any point in time -- if the process crashes in between, no children can be left behind whose ``parententry`` points to an already deleted node (orphaned entries). A repeated call simply continues the work (idempotent). Frozen carts belong to an order and cannot be removed. This also checks upfront whether the node is locked by a ``RelationalConsistency.PreventDeletion`` relation (e.g. an order referencing an in-progress checkout cart that has not been frozen yet) and fails before touching any child -- deleting the children first and finding out only at ``skel.delete()`` that the node itself is locked would leave the order pointing at an emptied-out cart. :param cart_key: Key of the cart node to remove. :raises errors.NotFound: If the cart node does not exist. :raises errors.Forbidden: If the cart node is frozen. :raises errors.Locked: If the cart node is referenced by a PreventDeletion relation (e.g. an order). .. py:method:: _delete_children(parent_cart_key) Delete all children of a cart node bottom-up and synchronously. Leafs of *parent_cart_key* are deleted first, then each sub-node is processed recursively (its own children before the sub-node itself). Deliberately does not call the inherited :meth:`deleteRecursive` of the Tree prototype: that method is ``@CallDeferred`` and only deletes the children, while the node itself is deleted synchronously by the caller right away (see :meth:`Tree.delete` / :meth:`cart_remove`). If that deferred task gets lost (queue purge, crash, or the task being pinned to an App Engine version that no longer exists), the node is gone but its children survive it forever -- orphaned entries whose broken ``parententry`` chain crashes price computations and ``update_relations`` tasks later. Cart trees are small (a handful of nodes/leafs), so there is no need to defer this work at all; running it synchronously, bottom-up and before the node itself is deleted removes the race entirely instead of just narrowing it. :param parent_cart_key: Key of the cart node whose subtree gets deleted. .. 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) Collect the discounts of all cart nodes above the given leaf. Walks the ``parententry`` chain from the leaf up to the root node and collects the ``discount`` relation of every node on the way. The walk is tolerant against broken tree data: if a parent node does not exist anymore (orphaned entry) or the chain contains a cycle, the walk stops at the broken link with a warning and the discounts collected so far are returned. This keeps computed bones (e.g. the price) usable on orphaned entries instead of raising and thereby crashing deferred tasks (like ``update_relations``) forever. :param leaf_key_or_skel: Key or skeleton of the cart leaf to start from. :return: The discount ref-skels found on the ancestor nodes. .. py:method:: add_new_parent(leaf_skel, **kwargs) .. py:method:: get_cached_cart_skel(key) Read a cart node skeleton with request-local caching. :param key: Key of the cart node to read. :return: The cached or freshly read node skeleton or ``None`` if the node does not exist (anymore). Missing nodes are not cached, so a later call re-checks the datastore. .. py:method:: get_closest_node(start, condition = lambda skel: True) Walk the ``parententry`` chain upwards and return the first ancestor node that satisfies *condition*. The walk stops and returns ``None`` when * the root node is reached without a match, * the current entry has no ``parententry`` (detached entry), * a parent node does not exist anymore (orphaned entry) or * the ``parententry`` chain contains a cycle. The latter cases are broken tree states which must not crash callers like the price computation running inside deferred tasks. :param start: Leaf or node skeleton to start from (itself excluded). :param condition: Predicate evaluated for each ancestor node. :return: The closest matching ancestor node or ``None``. .. py:function:: delete_guest_cart(session) Delete carts from guest sessions to avoid orphaned carts. Runs as :meth:`Session.on_delete` hook. Any failure is logged and swallowed: this hook must never prevent the session deletion itself. Carts that are referenced by an order, already gone or frozen are left alone.