def test_customer_usage_limit(rf): default_price = 10 request, product = _init_test_for_product_without_basket(rf, default_price) shop = request.shop customers = [] for x in range(3): customers.append(factories.create_random_company(shop=shop)) discount_percentage = 0.20 coupon = CouponCode.objects.create(code="sUpErAle", active=True, usage_limit_customer=2) coupon.shops = [request.shop] discount = Discount.objects.create( active=True, product=product, coupon_code=coupon, discount_percentage=discount_percentage) discount.shops.add(request.shop) # Order product twice for each customer for customer in customers: for y in range(2): _create_order(request, customer, coupon, product, request.shop.create_price(8)) assert coupon.usages.count() == 6 # Each customer 2 orders # Any of the customers created shouldn't be allowed to # order more with this coupon code. for customer in customers: assert not CouponCode.is_usable(shop, coupon, customer) # New customer should still be able to order some new_customer = factories.create_random_person() assert CouponCode.is_usable(shop, coupon, new_customer) _create_order(request, new_customer, coupon, product, request.shop.create_price(8)) assert coupon.usages.count() == 7 # Set usage limit and the new customer shouldn't be able to use the code coupon.usage_limit = 7 coupon.save() assert not CouponCode.is_usable(request.shop, coupon, new_customer) _create_order(request, new_customer, coupon, product, request.shop.create_price(default_price)) assert coupon.usages.count() == 7 # One of the customer got refund refunded_customer = customers[0] order = refunded_customer.customer_orders.first() coupon_code_modifier = CouponCodeModule() coupon_code_modifier.clear_codes(order) assert coupon.usages.count() == 6 # New customer still doesn't able to create coupon new_customer = factories.create_random_person() assert CouponCode.is_usable(shop, coupon, new_customer) _create_order(request, new_customer, coupon, product, request.shop.create_price(8)) assert coupon.usages.count() == 7
def discount_price(self, context, product, price_info): shop = context.shop basket = getattr(context, "basket", None) potential_discounts = get_potential_discounts_for_product( context, product).values_list("discounted_price_value", "discount_amount_value", "discount_percentage", "coupon_code__code") discounted_prices = [] for discounted_price_value, discount_amount_value, discount_percentage, coupon_code in potential_discounts: if basket and coupon_code and not CouponCode.is_usable( shop, coupon_code, customer=basket.customer): # TODO: Revise! This will cause some queries. Are do we want those? Maybe some cache for this check? # Maybe somewhere we should just remove coupon codes that is not usable from basket all together? continue if discounted_price_value: # Applies the new product price per item discounted_prices.append( min( price_info.price, max( shop.create_price(discounted_price_value) * price_info.quantity, shop.create_price(0)))) if discount_amount_value: # Discount amount value per item discounted_prices.append( max( price_info.price - shop.create_price(discount_amount_value) * price_info.quantity, shop.create_price(0))) if discount_percentage: # Discount percentage per item discounted_prices.append( max( price_info.price - price_info.price * discount_percentage, shop.create_price(0))) if discounted_prices: minimum_price_values = list( ShopProduct.objects.filter(product_id=product.pk, shop=shop).values_list( "minimum_price_value", flat=True)) minimum_price_value = minimum_price_values[ 0] if minimum_price_values else 0 price_info.price = max( min(discounted_prices), shop.create_price(minimum_price_value or 0) or shop.create_price(0)) price_expiration = get_price_expiration(context, product) if price_expiration and (not price_info.expires_on or price_expiration < price_info.expires_on): price_info.expires_on = price_expiration return price_info
def discount_price(self, context, product, price_info): shop = context.shop basket = getattr(context, "basket", None) potential_discounts = get_potential_discounts_for_product(context, product).values_list( "discounted_price_value", "discount_amount_value", "discount_percentage", "coupon_code__code" ) discounted_prices = [] for discounted_price_value, discount_amount_value, discount_percentage, coupon_code in potential_discounts: if basket and coupon_code and not CouponCode.is_usable(shop, coupon_code, customer=basket.customer): # TODO: Revise! This will cause some queries. Are do we want those? Maybe some cache for this check? # Maybe somewhere we should just remove coupon codes that is not usable from basket all together? continue if discounted_price_value: # Applies the new product price per item discounted_prices.append( min( price_info.price, max( shop.create_price(discounted_price_value) * price_info.quantity, shop.create_price(0) ) ) ) if discount_amount_value: # Discount amount value per item discounted_prices.append( max( price_info.price - shop.create_price(discount_amount_value) * price_info.quantity, shop.create_price(0) ) ) if discount_percentage: # Discount percentage per item discounted_prices.append( max( price_info.price - price_info.price * discount_percentage, shop.create_price(0) ) ) if discounted_prices: product_id = (product if isinstance(product, six.integer_types) else product.pk) minimum_price_values = list(ShopProduct.objects.filter( product_id=product_id, shop=shop).values_list("minimum_price_value", flat=True)) minimum_price_value = minimum_price_values[0] if minimum_price_values else 0 price_info.price = max( min(discounted_prices), shop.create_price(minimum_price_value or 0) or shop.create_price(0) ) price_expiration = get_price_expiration(context, product) if price_expiration and (not price_info.expires_on or price_expiration < price_info.expires_on): price_info.expires_on = price_expiration return price_info
def test_usage_limit(rf): default_price = 10 request, product, basket = _init_test_for_product_with_basket(rf, default_price) discounted_price = 4 coupon_code = "TEST!2" shop = request.shop coupon = CouponCode.objects.create(code=coupon_code, active=True) coupon.shops = [shop] discount = Discount.objects.create( active=True, product=product, coupon_code=coupon, discounted_price_value=discounted_price) discount.shops.add(request.shop) # Can not use coupon code that does not exist assert not CouponCode.is_usable(shop, "SIMO", basket.customer) # The coupon code should be usable assert CouponCode.is_usable(shop, coupon_code, basket.customer) assert coupon.can_use_code(shop, basket.customer) # Can not add coupon code that is not active coupon.active = False coupon.save() assert not CouponCode.is_usable(shop, coupon_code, basket.customer) assert not coupon.can_use_code(shop, basket.customer) # Re-activate coupon code coupon.active = True coupon.save() assert CouponCode.is_usable(shop, coupon_code, basket.customer) assert coupon.can_use_code(shop, basket.customer) # Can not use coupon code that is not attached discount.coupon_code = None discount.save() assert not CouponCode.is_usable(shop, coupon_code, basket.customer) # Re-attach discount discount.coupon_code = coupon discount.save() assert CouponCode.is_usable(shop, coupon_code, basket.customer) # Coupon code needs to be attached to current shop shop2 = factories.get_shop(prices_include_tax=True) assert not CouponCode.is_usable(shop2, coupon_code, basket.customer) assert CouponCode.is_usable(shop, coupon_code, basket.customer) coupon.shops.clear() assert not CouponCode.is_usable(shop, coupon_code, basket.customer) assert not coupon.can_use_code(shop, basket.customer) coupon.shops = [shop] assert CouponCode.is_usable(shop, coupon_code, basket.customer) assert coupon.can_use_code(shop, basket.customer) # Coupon code not yet added to basket even if it is usable assert product.get_price_info(request).price == request.shop.create_price(default_price) basket.add_code(coupon) assert product.get_price_info(request).price == request.shop.create_price(discounted_price) order = factories.create_random_order() for x in range(50): coupon.use(order) assert coupon.usage_limit is None assert coupon.usages.count() == 50 assert product.get_price_info(request).price == request.shop.create_price(discounted_price) # Set coupon usage limit 50 coupon.usage_limit = 50 coupon.save() assert not CouponCode.is_usable(shop, coupon.code, order.customer) assert product.get_price_info(request).price == request.shop.create_price(default_price) # Increase limit by 5 coupon.usage_limit = 55 coupon.save() for x in range(5): assert product.get_price_info(request).price == request.shop.create_price(discounted_price) coupon.use(order) assert coupon.usages.count() == 55 assert not CouponCode.is_usable(shop, coupon.code, order.customer) assert product.get_price_info(request).price == request.shop.create_price(default_price)