Source code for shuup.core.basket.update_methods

# -*- coding: utf-8 -*-
# This file is part of Shuup.
#
# Copyright (c) 2012-2021, Shuup Commerce Inc. All rights reserved.
#
# This source code is licensed under the OSL-3.0 license found in the
# LICENSE file in the root directory of this source tree.
from __future__ import unicode_literals

import six
from django.core.exceptions import ValidationError

from shuup.core.models import ShopProduct
from shuup.core.order_creator import OrderLineBehavior
from shuup.utils.numbers import parse_decimal_string


[docs]class BasketUpdateMethods(object): def __init__(self, request, basket): """ Initialize. :type request: django.http.HttpRequest :type basket: shuup.front.basket.objects.BaseBasket """ self.request = request self.basket = basket
[docs] def get_prefix_to_method_map(self): """Override this method to link prefixes with their associated methods to call. Format of the dictionary is: { FIELD_NAME_PREFIX: METHOD }. METHOD is a function which accepts the keyword arguments given in `update_basket_contents`. It should perform the necessary changes to the basket_line and then return whether the value had changed or not. (See `update_quantity` or `delete_line` for examples.) """ return { "q_": self.update_quantity, "dq_": self.update_display_quantity, "delete_": self.delete_line, }
[docs] def delete_line(self, line, **kwargs): if line: return self.basket.delete_line(line["line_id"])
def _get_orderability_errors(self, product, supplier, delta): basket_quantities = self.basket.get_product_ids_and_quantities() product_basket_quantity = basket_quantities.get(product.id, 0) total_product_quantity = delta + product_basket_quantity try: shop_product = product.get_shop_instance(shop=self.request.shop) except ShopProduct.DoesNotExist: return [ ValidationError( "Error! %s is not available in %s." % (product, self.request.shop), code="product_not_available_in_shop", ) ] errors = list( shop_product.get_orderability_errors( supplier=supplier, customer=self.basket.customer, quantity=total_product_quantity ) ) if product.is_package_parent(): for child_product, child_quantity in six.iteritems(product.get_package_child_to_quantity_map()): child_basket_quantity = basket_quantities.get(child_product.id, 0) total_child_quantity = (delta * child_quantity) + child_basket_quantity try: shop_product = child_product.get_shop_instance(shop=self.request.shop) except ShopProduct.DoesNotExist: child_errors = [ ValidationError( "Error! %s is not available in %s." % (child_product, self.request.shop), code="product_not_available_in_shop", ) ] else: child_errors = list( shop_product.get_orderability_errors( supplier=supplier, customer=self.basket.customer, quantity=total_child_quantity ) ) errors.extend(child_errors) return errors
[docs] def update_display_quantity(self, line, value, **kwargs): if not line: return False new_display_quantity = parse_decimal_string(value) if new_display_quantity is None: return False basket_line = self.basket.get_basket_line(line["line_id"]) if basket_line and basket_line.product: unit = basket_line.shop_product.unit new_quantity = unit.from_display(new_display_quantity) else: new_quantity = new_display_quantity return self._update_quantity(line, new_quantity)
[docs] def update_quantity(self, line, value, **kwargs): new_quantity = parse_decimal_string(value) if new_quantity is None: return False return self._update_quantity(line, new_quantity)
def _handle_orderability_error(self, line, error): raise error def _update_quantity(self, line, new_quantity): if not (line and line["quantity"] != new_quantity): return False changed = False # Ensure sub-lines also get changed accordingly linked_lines = [line] + list(self.basket.find_lines_by_parent_line_id(line["line_id"])) orderable_lines = {basket_line.line_id: basket_line for basket_line in self.basket.get_lines()} for linked_line in linked_lines: orderable_line = orderable_lines.get(linked_line["line_id"]) # Use default OrderLineBehaviour line_behavior = linked_line.get("on_parent_change_behavior", OrderLineBehavior.INHERIT) if not orderable_line: # Customer can change quantity in non-orderable lines regardless linked_line["quantity"] = new_quantity changed = True elif line_behavior == OrderLineBehavior.DELETE: linked_line["quantity"] = 0 changed = True elif line_behavior == OrderLineBehavior.SKIP: continue # then line_behavior == OrderLineBehavior.INHERIT else: product = orderable_line.product supplier = orderable_line.supplier # Basket quantities already contain current quantities for orderable lines quantity_delta = new_quantity - line["quantity"] # check orderability errors if the line contains a product if product: errors = self._get_orderability_errors(product, supplier, quantity_delta) if errors: for error in errors: self._handle_orderability_error(line, error) continue self.basket.update_line(linked_line, quantity=new_quantity) linked_line["quantity"] = new_quantity changed = True return changed