def test_parallel_baskets(rf): request = get_request_with_basket() shop = get_default_shop() customer = create_random_person() request = rf.get("/") request.shop = shop apply_request_middleware(request) request.customer = customer basket_one = get_basket(request, basket_name="basket_one") basket_two = get_basket(request, basket_name="basket_two") product_one = get_default_product() product_two = get_default_product() product_two.sku = "derpy-hooves" sales_unit = SalesUnit.objects.create(identifier="test-sales-partial", decimals=2, name="Partial unit") product_two.sales_unit = sales_unit # Set the sales unit for the product product_two.save() basket_commands.handle_add(request, basket_one, product_id=product_one.pk, quantity=1) basket_commands.handle_add(request, basket_two, product_id=product_two.pk, quantity=3.5) assert basket_one.product_count == 1 assert basket_two.product_count == 3.5
def test_basket(rf, storage): StoredBasket.objects.all().delete() quantities = [3, 12, 44, 23, 65] shop = get_default_shop() get_default_payment_method() # Can't create baskets without payment methods supplier = get_default_supplier() products_and_quantities = [] for quantity in quantities: product = create_product(printable_gibberish(), shop=shop, supplier=supplier, default_price=50) products_and_quantities.append((product, quantity)) is_database = (storage == "shuup.front.basket.storage:DatabaseBasketStorage") with override_settings(SHUUP_BASKET_STORAGE_CLASS_SPEC=storage): for product, q in products_and_quantities: request = rf.get("/") request.session = {} request.shop = shop apply_request_middleware(request) basket = get_basket(request) assert basket == request.basket assert basket.product_count == 0 line = basket.add_product(supplier=supplier, shop=shop, product=product, quantity=q) basket.shipping_method = get_shipping_method(shop=shop) # For shippable product assert line.quantity == q assert basket.get_lines() assert basket.get_product_ids_and_quantities().get(product.pk) == q assert basket.product_count == q basket.save() delattr(request, "basket") basket = get_basket(request) assert basket.get_product_ids_and_quantities().get(product.pk) == q if is_database: product_ids = set(StoredBasket.objects.last().products.values_list("id", flat=True)) assert product_ids == set([product.pk]) if is_database: stats = StoredBasket.objects.all().aggregate( n=Sum("product_count"), tfs=Sum("taxful_total_price_value"), tls=Sum("taxless_total_price_value"), ) assert stats["n"] == sum(quantities) if shop.prices_include_tax: assert stats["tfs"] == sum(quantities) * 50 else: assert stats["tls"] == sum(quantities) * 50 basket.finalize()
def test_stripe_checkout_phase(rf, stripe_payment_processor): request = rf.get("/") request.shop = get_default_shop() request.session = {} request.basket = get_basket(request) service = stripe_payment_processor.create_service( None, shop=request.shop, tax_class=get_default_tax_class(), enabled=True) checkout_phase = StripeCheckoutPhase( request=request, service=service) assert not checkout_phase.is_valid() # We can't be valid just yet context = checkout_phase.get_context_data() assert context["stripe"] request.method = "POST" token = get_stripe_token(stripe_payment_processor) request.POST = { "stripeToken": token["id"], "stripeTokenType": token["type"], "stripeTokenEmail": token.get("email"), } checkout_phase.post(request) assert checkout_phase.is_valid() # We should be valid now assert request.session # And things should've been saved into the session checkout_phase.process() # And this should do things to the basket assert request.basket.payment_data.get("stripe")
def test_productfilter_works(rf): request, shop, group = initialize_test(rf, False) price = shop.create_price product_price = "100" discount_percentage = "0.30" supplier = get_default_supplier() product = create_product(printable_gibberish(), shop=shop, supplier=supplier, default_price=product_price) shop_product = product.get_shop_instance(shop) # create catalog campaign catalog_filter = ProductFilter.objects.create() catalog_filter.products.add(product) assert catalog_filter.matches(shop_product) catalog_campaign = CatalogCampaign.objects.create(shop=shop, active=True, name="test") catalog_campaign.filters.add(catalog_filter) cdp = ProductDiscountPercentage.objects.create(campaign=catalog_campaign, discount_percentage=discount_percentage) # add product to basket basket = get_basket(request) basket.add_product(supplier=supplier, shop=shop, product=product, quantity=1) basket.shipping_method = get_shipping_method(shop=shop) basket.save() expected_total = price(product_price) - (Decimal(discount_percentage) * price(product_price)) assert basket.total_price == expected_total
def test_basket_shipping_error(rf): StoredBasket.objects.all().delete() shop = get_default_shop() supplier = get_default_supplier() shipped_product = create_product( printable_gibberish(), shop=shop, supplier=supplier, default_price=50, shipping_mode=ShippingMode.SHIPPED ) unshipped_product = create_product( printable_gibberish(), shop=shop, supplier=supplier, default_price=50, shipping_mode=ShippingMode.NOT_SHIPPED ) request = rf.get("/") request.session = {} request.shop = shop apply_request_middleware(request) basket = get_basket(request) # With a shipped product but no shipping methods, we oughta get an error basket.add_product(supplier=supplier, shop=shop, product=shipped_product, quantity=1) assert any(ve.code == "no_common_shipping" for ve in basket.get_validation_errors()) basket.clear_all() # But with an unshipped product, we should not basket.add_product(supplier=supplier, shop=shop, product=unshipped_product, quantity=1) assert not any(ve.code == "no_common_shipping" for ve in basket.get_validation_errors())
def test_campaign_with_coupons1(rf): basket, dc, request, status = _init_basket_coupon_test(rf) assert len(basket.get_final_lines()) == 3 # no discount was applied because coupon is required basket.add_code(dc.code) assert len(basket.get_final_lines()) == 4 # now basket has codes so they will be applied too assert OrderLineType.DISCOUNT in [l.type for l in basket.get_final_lines()] # Ensure codes persist between requests, so do what the middleware would, i.e. basket.save() # and then reload the basket: del request.basket basket = get_basket(request) assert basket.codes == [dc.code] assert len(basket.get_final_lines()) == 3 # now basket has codes so they will be applied too assert OrderLineType.DISCOUNT in [l.type for l in basket.get_final_lines()] basket.status = status creator = OrderCreator(request) order = creator.create_order(basket) assert CouponUsage.objects.filter(order=order).count() == 1 assert CouponUsage.objects.filter(order=order, coupon__code=dc.code).count() == 1
def test_basket_orderability_change_shop_product(rf): StoredBasket.objects.all().delete() shop = get_default_shop() supplier = get_default_supplier() product = create_product(printable_gibberish(), shop=shop, supplier=supplier, default_price=50) request = rf.get("/") request.session = {} request.shop = shop apply_request_middleware(request) basket = get_basket(request) line = basket.add_product( supplier=supplier, shop=shop, product=product, quantity=1, force_new_line=True, extra={"foo": "foo"} ) assert len(basket.get_lines()) == 1 assert len(basket.get_unorderable_lines()) == 0 product.get_shop_instance(shop).delete() basket.uncache() assert basket.dirty assert len(basket.get_lines()) == 0 assert len(basket.get_unorderable_lines()) == 1
def test_product_in_basket_condition(rf): request, shop, group = initialize_test(rf, False) basket = get_basket(request) supplier = get_default_supplier() product = create_product("Just-A-Product-Too", shop, default_price="200") basket.add_product(supplier=supplier, shop=shop, product=product, quantity=1) condition = ProductsInBasketCondition.objects.create() condition.values = [product] condition.save() assert condition.values.first() == product assert condition.matches(basket, []) condition.quantity = 2 condition.save() assert not condition.matches(basket, []) basket.add_product(supplier=supplier, shop=shop, product=product, quantity=1) assert condition.matches(basket, []) condition.operator = ComparisonOperator.EQUALS condition.save() assert condition.matches(basket, []) basket.add_product(supplier=supplier, shop=shop, product=product, quantity=1) assert not condition.matches(basket, [])
def test_category_product_in_basket_condition(rf): request, shop, group = initialize_test(rf, False) basket = get_basket(request) supplier = get_default_supplier() category = get_default_category() product = create_product("The Product", shop=shop, default_price="200", supplier=supplier) basket.add_product(supplier=supplier, shop=shop, product=product, quantity=1) basket.shipping_method = get_shipping_method(shop=shop) shop_product = product.get_shop_instance(shop) assert category not in shop_product.categories.all() condition = CategoryProductsBasketCondition.objects.create(operator=ComparisonOperator.EQUALS, quantity=1) condition.categories.add(category) # No match the product does not have the category assert not condition.matches(basket, []) category.shop_products.add(shop_product) assert condition.matches(basket, []) basket.add_product(supplier=supplier, shop=shop, product=product, quantity=1) assert not condition.matches(basket, []) condition.operator = ComparisonOperator.GTE condition.save() assert condition.matches(basket, []) condition.excluded_categories.add(category) assert not condition.matches(basket, []) with pytest.raises(CampaignsInvalidInstanceForCacheUpdate): update_filter_cache("test", shop)
def test_contact_group_basket_condition_with_none(rf): request = apply_request_middleware(rf.get("/")) request.shop = Shop() basket = get_basket(request) condition = ContactGroupBasketCondition.objects.create() result = condition.matches(basket) # Should not raise any errors assert result is False
def test_only_cheapest_price_is_selected(rf): request, shop, group = initialize_test(rf, False) price = shop.create_price basket = get_basket(request) supplier = get_default_supplier() # create a basket rule that requires atleast value of 200 rule = BasketTotalAmountCondition.objects.create(value="200") product_price = "200" discount1 = "10" discount2 = "20" # should be selected product = create_product(printable_gibberish(), shop=shop, supplier=supplier, default_price=product_price) basket.add_product(supplier=supplier, shop=shop, product=product, quantity=1) campaign1 = BasketCampaign.objects.create(shop=shop, public_name="test", name="test", active=True) campaign1.conditions.add(rule) campaign1.save() BasketDiscountAmount.objects.create(discount_amount=discount1, campaign=campaign1) campaign2 = BasketCampaign.objects.create(shop=shop, public_name="test", name="test", active=True) campaign2.conditions.add(rule) campaign2.save() BasketDiscountAmount.objects.create(discount_amount=discount2, campaign=campaign2) assert len(basket.get_final_lines()) == 2 line_types = [l.type for l in basket.get_final_lines()] assert OrderLineType.DISCOUNT in line_types for line in basket.get_final_lines(): if line.type == OrderLineType.DISCOUNT: assert line.discount_amount == price(discount2)
def test_category_product_in_basket_condition(rf): request, shop, group = initialize_test(rf, False) basket = get_basket(request) supplier = get_default_supplier() category = get_default_category() product = create_product("The Product", shop=shop, default_price="200", supplier=supplier) basket.add_product(supplier=supplier, shop=shop, product=product, quantity=1) shop_product = product.get_shop_instance(shop) assert category not in shop_product.categories.all() condition = CategoryProductsBasketCondition.objects.create( category=category, operator=ComparisonOperator.EQUALS, quantity=1) # No match the product does not have the category assert not condition.matches(basket, []) shop_product.categories.add(category) assert condition.matches(basket, []) basket.add_product(supplier=supplier, shop=shop, product=product, quantity=1) assert not condition.matches(basket, []) condition.operator = ComparisonOperator.GTE condition.save() assert condition.matches(basket, [])
def test_percentage_campaign(rf): request, shop, group = initialize_test(rf, False) price = shop.create_price basket = get_basket(request) supplier = get_default_supplier() # create a basket rule that requires at least value of 200 rule = BasketTotalAmountCondition.objects.create(value="200") product_price = "200" discount_percentage = "0.1" expected_discounted_price = price(product_price) - (price(product_price) * Decimal(discount_percentage)) product = create_product(printable_gibberish(), shop=shop, supplier=supplier, default_price=product_price) basket.add_product(supplier=supplier, shop=shop, product=product, quantity=1) campaign = BasketCampaign.objects.create( shop=shop, public_name="test", name="test", active=True) campaign.conditions.add(rule) campaign.save() BasketDiscountPercentage.objects.create(campaign=campaign, discount_percentage=discount_percentage) assert len(basket.get_final_lines()) == 2 assert basket.product_count == 1 assert basket.total_price == expected_discounted_price
def test_product_in_basket_condition_with_variation_parent(rf): request, shop, group = initialize_test(rf, False) basket = get_basket(request) supplier = get_default_supplier() product = create_product( "test-product", shop, default_price="200", supplier=supplier, mode=ProductMode.SIMPLE_VARIATION_PARENT) child_products = [] for x in range(0, 3): child_product = create_product("test-product-%s" % x, shop, default_price="10", supplier=supplier) child_product.link_to_parent(product) child_products.append(child_product) condition = ProductsInBasketCondition.objects.create() condition.values = [product] condition.operator = ComparisonOperator.EQUALS condition.quantity = 3 condition.save() assert not condition.matches(basket, []) for child_product in child_products: basket.add_product(supplier=supplier, shop=shop, product=child_product, quantity=1) assert condition.matches(basket, [])
def _get_order_with_coupon(request, initial_status, condition_product_count=1): shop = request.shop basket = get_basket(request) supplier = get_default_supplier() product = create_product(printable_gibberish(), shop=shop, supplier=supplier, default_price="50") basket.add_product(supplier=supplier, shop=shop, product=product, quantity=1) basket.shipping_method = get_shipping_method(shop=shop) # For shippable products dc = Coupon.objects.create(code="TEST", active=True) campaign = BasketCampaign.objects.create( shop=shop, name="test", public_name="test", coupon=dc, active=True ) BasketDiscountAmount.objects.create(discount_amount=shop.create_price("20"), campaign=campaign) rule = BasketTotalProductAmountCondition.objects.create(value=1) campaign.conditions.add(rule) campaign.save() basket.add_code(dc.code) basket.save() basket.status = initial_status creator = OrderCreator(request) order = creator.create_order(basket) assert order.lines.count() == 3 assert OrderLineType.DISCOUNT in [l.type for l in order.lines.all()] return order
def test_basket_total_undiscounted_value_conditions(rf): request, shop, group = initialize_test(rf, False) basket = get_basket(request) supplier = get_default_supplier() product = create_product("Just-A-Product", shop, default_price="150", supplier=supplier) discounted_product = create_product("Just-A-Second-Product", shop, default_price="200", supplier=supplier) # CatalogCampaign catalog_campaign = CatalogCampaign.objects.create(active=True, shop=shop, name="test", public_name="test") # Limit catalog campaign to "discounted_product" product_filter = ProductFilter.objects.create() product_filter.products.add(discounted_product) catalog_campaign.filters.add(product_filter) basket.add_product(supplier=supplier, shop=shop, product=discounted_product, quantity=1) condition = BasketTotalUndiscountedProductAmountCondition.objects.create() condition.value = 1 condition.save() assert not condition.matches(basket, []) basket.add_product(supplier=supplier, shop=shop, product=product, quantity=1) assert condition.matches(basket, []) # Too high amount for undiscounted value condition.value = 151 condition.save() assert not condition.matches(basket, [])
def basket(self): """ The basket used in this checkout process. :rtype: shuup.front.basket.objects.BaseBasket """ return get_basket(self.request)
def test_basket_total_amount_conditions(rf): request, shop, group = initialize_test(rf, False) basket = get_basket(request) supplier = get_default_supplier() product = create_product("Just-A-Product-Too", shop, default_price="200") basket.add_product(supplier=supplier, shop=shop, product=product, quantity=1) condition = BasketTotalAmountCondition.objects.create() condition.value = 1 condition.save() assert condition.value == 1 assert condition.matches(basket, []) condition2 = BasketMaxTotalAmountCondition.objects.create() condition2.value = 200 condition2.save() assert condition2.matches(basket, []) condition2.value = 199 condition2.save() assert not condition2.matches(basket, [])
def test_basket_campaign_with_multiple_supppliers(rf): request, shop, group = initialize_test(rf, False) supplier1 = Supplier.objects.create(identifier="1") supplier2 = Supplier.objects.create(identifier="2") price = shop.create_price basket = get_basket(request) single_product_price = "50" discount_amount_supplier1 = "10" discount_amount_supplier2 = "40" product1 = create_product("product1", shop=shop, supplier=supplier1, default_price=single_product_price) product2 = create_product("product2", shop=shop, supplier=supplier2, default_price=single_product_price) basket.add_product(supplier=supplier1, shop=shop, product=product1, quantity=1) basket.add_product(supplier=supplier2, shop=shop, product=product2, quantity=1) basket.shipping_method = get_shipping_method(shop=shop) basket.save() assert basket.product_count == 2 assert basket.total_price.value == 100 # Create campaign for supplier one basket_rule1 = ProductsInBasketCondition.objects.create(quantity=1) basket_rule1.products.add(product1) campaign = BasketCampaign.objects.create( shop=shop, public_name="test", name="test", active=True, supplier=supplier1) campaign.conditions.add(basket_rule1) campaign.save() BasketDiscountAmount.objects.create( campaign=campaign, discount_amount=discount_amount_supplier1) basket.uncache() lines = basket.get_final_lines() assert len(lines) == 4 assert basket.total_price.value == 90 # 10d discount from the supplier1 product line = _get_discount_line(lines, 10) assert line.supplier == supplier1 # Create campaign for supplier two basket_rule2 = ProductsInBasketCondition.objects.create(quantity=1) basket_rule2.products.add(product2) campaign = BasketCampaign.objects.create( shop=shop, public_name="test", name="test", active=True, supplier=supplier2) campaign.conditions.add(basket_rule2) campaign.save() BasketDiscountAmount.objects.create( campaign=campaign, discount_amount=discount_amount_supplier2) basket.uncache() lines = basket.get_final_lines() assert len(lines) == 5 assert basket.total_price.value == 50 # -10d - 40d from 100d line = _get_discount_line(lines, 40) assert line.supplier == supplier2
def test_campaign_with_coupons(rf): status = get_initial_order_status() request, shop, group = initialize_test(rf, False) basket = get_basket(request) supplier = get_default_supplier() for x in range(2): product = create_product(printable_gibberish(), shop, supplier=supplier, default_price="50") basket.add_product(supplier=supplier, shop=shop, product=product, quantity=1) basket.shipping_method = get_shipping_method(shop=shop) # For shippable products dc = Coupon.objects.create(code="TEST", active=True) campaign = BasketCampaign.objects.create( shop=shop, name="test", public_name="test", coupon=dc, active=True ) BasketDiscountAmount.objects.create(discount_amount=shop.create_price("20"), campaign=campaign) rule = BasketTotalProductAmountCondition.objects.create(value=2) campaign.conditions.add(rule) campaign.save() assert len(basket.get_final_lines()) == 3 # no discount was applied because coupon is required basket.add_code(dc.code) assert len(basket.get_final_lines()) == 4 # now basket has codes so they will be applied too assert OrderLineType.DISCOUNT in [l.type for l in basket.get_final_lines()] # Ensure codes persist between requests, so do what the middleware would, i.e. basket.save() # and then reload the basket: del request.basket basket = get_basket(request) assert basket.codes == [dc.code] assert len(basket.get_final_lines()) == 3 # now basket has codes so they will be applied too assert OrderLineType.DISCOUNT in [l.type for l in basket.get_final_lines()] basket.status = status creator = OrderCreator(request) order = creator.create_order(basket) assert CouponUsage.objects.filter(order=order).count() == 1 assert CouponUsage.objects.filter(order=order, coupon__code=dc.code).count() == 1
def test_basket_data_fields(rf): StoredBasket.objects.all().delete() shop = get_default_shop() request = rf.get("/") request.session = {} request.shop = shop apply_request_middleware(request) basket = get_basket(request) basket.shipping_data = {"shipment": True} basket.payment_data = {"payment": True} basket.extra_data = {"extra": True} basket.save() request.basket = None request.baskets = None basket = get_basket(request) assert basket.shipping_data["shipment"] is True assert basket.payment_data["payment"] is True assert basket.extra_data["extra"] is True
def test_basket_package_product_orderability_change(rf): if "shuup.simple_supplier" not in settings.INSTALLED_APPS: pytest.skip("Need shuup.simple_supplier in INSTALLED_APPS") from shuup_tests.simple_supplier.utils import get_simple_supplier StoredBasket.objects.all().delete() shop = get_default_shop() supplier = get_simple_supplier() product, child = get_unstocked_package_product_and_stocked_child(shop, supplier, child_logical_quantity=2) request = rf.get("/") request.session = {} request.shop = shop apply_request_middleware(request) basket = get_basket(request) # Add the package parent basket.add_product( supplier=supplier, shop=shop, product=product, quantity=1, force_new_line=True, extra={"foo": "foo"} ) # Also add the child product separately basket.add_product( supplier=supplier, shop=shop, product=child, quantity=1, force_new_line=True, extra={"foo": "foo"} ) # Should be stock for both assert len(basket.get_lines()) == 2 assert len(basket.get_unorderable_lines()) == 0 supplier.adjust_stock(child.id, -1) # Orderability is already cached, we need to uncache to force recheck basket.uncache() # After reducing stock to 1, should only be stock for one assert len(basket.get_lines()) == 1 assert len(basket.get_unorderable_lines()) == 1 supplier.adjust_stock(child.id, -1) basket.uncache() # After reducing stock to 0, should be stock for neither assert len(basket.get_lines()) == 0 assert len(basket.get_unorderable_lines()) == 2
def test_basket_free_product(rf): request, shop, _ = initialize_test(rf, False) basket = get_basket(request) supplier = get_default_supplier() single_product_price = "50" original_quantity = 2 # create basket rule that requires 2 products in basket product = create_product(printable_gibberish(), shop=shop, supplier=supplier, default_price=single_product_price) basket.add_product(supplier=supplier, shop=shop, product=product, quantity=2) basket.shipping_method = get_shipping_method(shop=shop) basket.save() second_product = create_product(printable_gibberish(), shop=shop, supplier=supplier, default_price=single_product_price) # no shop third_product = create_product(printable_gibberish(), supplier=supplier) rule = BasketTotalProductAmountCondition.objects.create(value="2") campaign = BasketCampaign.objects.create(active=True, shop=shop, name="test", public_name="test") campaign.conditions.add(rule) effect = FreeProductLine.objects.create(campaign=campaign, quantity=2) effect.products.add(second_product) discount_lines_count = len(effect.get_discount_lines(basket, [], supplier)) assert discount_lines_count == 1 # do not affect as there is no shop product for the product effect.products.add(third_product) assert len(effect.get_discount_lines(basket, [], supplier)) == discount_lines_count basket.uncache() final_lines = basket.get_final_lines() assert len(final_lines) == 3 line_types = [l.type for l in final_lines] assert OrderLineType.DISCOUNT not in line_types for line in basket.get_final_lines(): assert line.type in [OrderLineType.PRODUCT, OrderLineType.SHIPPING] if line.type == OrderLineType.SHIPPING: continue if line.product != product: assert line.product == second_product assert line.line_source == LineSource.DISCOUNT_MODULE assert line.quantity == original_quantity else: assert line.line_source == LineSource.CUSTOMER
def test_multiple_campaigns_match_with_coupon(rf): request, shop, group = initialize_test(rf, False) price = shop.create_price basket = get_basket(request) supplier = get_default_supplier() # create a basket rule that requires atleast value of 200 rule = BasketTotalAmountCondition.objects.create(value="200") product_price = "200" discount1 = "10" discount2 = "20" product = create_product(printable_gibberish(), shop=shop, supplier=supplier, default_price=product_price) basket.add_product(supplier=supplier, shop=shop, product=product, quantity=1) basket.shipping_method = get_shipping_method(shop=shop) campaign = BasketCampaign.objects.create(shop=shop, public_name="test", name="test", active=True) campaign.conditions.add(rule) campaign.save() BasketDiscountAmount.objects.create(discount_amount=discount1, campaign=campaign) dc = Coupon.objects.create(code="TEST", active=True) campaign2 = BasketCampaign.objects.create( shop=shop, public_name="test", name="test", coupon=dc, active=True ) BasketDiscountAmount.objects.create(discount_amount=discount2, campaign=campaign2) basket.add_product(supplier=supplier, shop=shop, product=product, quantity=1) resp = handle_add_campaign_code(request, basket, dc.code) assert resp.get("ok") discount_lines_values = [line.discount_amount for line in basket.get_final_lines() if line.type == OrderLineType.DISCOUNT] assert price(discount1) in discount_lines_values assert price(discount2) in discount_lines_values assert basket.total_price == (price(product_price) * basket.product_count - price(discount1) - price(discount2)) assert basket.codes == [dc.code] # test code removal resp = handle_remove_campaign_code(request, basket, dc.code) assert resp.get("ok") assert basket.codes == [] discount_lines_values = [line.discount_amount for line in basket.get_final_lines() if line.type == OrderLineType.DISCOUNT] assert price(discount1) in discount_lines_values assert not price(discount2) in discount_lines_values
def test_undiscounted_effects(rf, include_tax): request, shop, _ = initialize_test(rf, include_tax) basket = get_basket(request) supplier = get_default_supplier() single_product_price = Decimal(50) discounted_product_quantity = 4 normal_priced_product_quantity = 2 discount_percentage = Decimal(0.2) # 20% discount_amount = basket.create_price(single_product_price * normal_priced_product_quantity * discount_percentage) category = CategoryFactory() discounted_product = create_product( printable_gibberish(), shop=shop, supplier=supplier, default_price=single_product_price) second_product = create_product( printable_gibberish(), shop=shop, supplier=supplier, default_price=single_product_price) ShopProduct.objects.get(shop=shop, product=discounted_product).categories.add(category) ShopProduct.objects.get(shop=shop, product=second_product).categories.add(category) basket.add_product(supplier=supplier, shop=shop, product=discounted_product, quantity=discounted_product_quantity) basket.add_product(supplier=supplier, shop=shop, product=second_product, quantity=normal_priced_product_quantity) basket.shipping_method = get_shipping_method(shop=shop) basket.save() # Store basket price before any campaigns exists original_price = basket.total_price # CatalogCampaign catalog_campaign = CatalogCampaign.objects.create(active=True, shop=shop, name="test", public_name="test") # Limit catalog campaign to "discounted_product" product_filter = ProductFilter.objects.create() product_filter.products.add(discounted_product) catalog_campaign.filters.add(product_filter) # BasketCampaign campaign = BasketCampaign.objects.create( active=True, shop=shop, name="test2", public_name="test2") final_lines = basket.get_final_lines() assert len(final_lines) == 3 # Discount based on undiscounted product values DiscountPercentageFromUndiscounted.objects.create(campaign=campaign, discount_percentage=discount_percentage) basket.uncache() final_lines = basket.get_final_lines() assert len(final_lines) == 4 discounted_basket_price = original_price - discount_amount assert basket.total_price.as_rounded() == discounted_basket_price.as_rounded()
def test_stripe_checkout_phase(rf): cache.clear() shop = factories.get_default_shop() contact = factories.create_random_person() request = apply_request_middleware(rf.post("/"), shop=get_default_shop()) request.session = {} request.basket = get_basket(request) payment_processor = StripeCheckoutPaymentProcessor.objects.create(secret_key="secret", publishable_key="12", name="Stripe") service = payment_processor.create_service("stripe", shop=request.shop, tax_class=get_default_tax_class(), enabled=True) checkout_phase = StripeCheckoutPhase(request=request, service=service) with mock.patch.object(checkout_phase, "get_context_data") as mocked_get_context_data: mocked_get_context_data.return_value = { "view": checkout_phase, "stripe": { "publishable_key": "ha", "name": "he", "description": "hi", }, "customer": contact, "stripe_customer_data": { "id": "testing", "sources": { "data": { "?": True } } } } response = checkout_phase.get(request) assert response.status_code == 200 response.render() content = response.content.decode("utf-8") assert "We use Stripe for secure payment handling. You will only be charged when your order completes" in content assert "Click the button below to enter your card details" in content assert "Use saved card details by clicking button below" in content set_checkout_payment_details_message(shop, "ABC123") set_checkout_payment_phase_message(shop, "XYZ987") set_checkout_saved_card_message(shop, "QWERTY456") response = checkout_phase.get(request) assert response.status_code == 200 response.render() content = response.content.decode("utf-8") assert "We use Stripe for secure payment handling. You will only be charged when your order completes" not in content assert "Click the button below to enter your card details" not in content assert "Use saved card details by clicking button below" not in content assert "ABC123" in content assert "XYZ987" in content assert "QWERTY456" in content
def test_stripe_checkout_phase_with_misconfigured_module(rf): stripe_payment_prossor = ( StripeCheckoutPaymentProcessor.objects.create()) request = rf.get("/") request.shop = get_default_shop() request.session = {} request.basket = get_basket(request) service = stripe_payment_prossor.create_service( None, shop=request.shop, tax_class=get_default_tax_class(), enabled=True) checkout_phase = StripeCheckoutPhase( request=request, service=service) with pytest.raises(Problem): checkout_phase.get_context_data()
def test_basket_category_discount(rf): """ Test that discounting based on product category works. """ request, shop, group = initialize_test(rf, False) price = shop.create_price basket = get_basket(request) supplier = get_default_supplier() category = CategoryFactory() discount_amount_value = 6 single_product_price = 10 def create_category_product(category): product = create_product(printable_gibberish(), shop, supplier, single_product_price) product.primary_category = category sp = ShopProduct.objects.get(product=product, shop=shop) sp.primary_category = category sp.categories.add(category) return product basket_condition = CategoryProductsBasketCondition.objects.create(quantity=2) basket_condition.categories.add(category) campaign = BasketCampaign.objects.create( shop=shop, public_name="test", name="test", active=True ) campaign.conditions.add(basket_condition) campaign.save() DiscountFromCategoryProducts.objects.create( campaign=campaign, discount_amount=discount_amount_value, category=category ) basket.save() products = [create_category_product(category) for i in range(2)] for product in products: basket.add_product(supplier=supplier, shop=shop, product=product, quantity=1) basket.shipping_method = get_shipping_method(shop=shop) basket.save() assert basket.product_count == 2 assert basket_condition.matches(basket=basket, lines=basket.get_lines()) assert campaign.rules_match(basket, basket.get_lines()) assert basket.total_price == price(single_product_price * 2) - price(discount_amount_value * 2)
def test_product_category_discount_amount_with_minimum_price(rf): # Buy X amount of Y get Z discount from Y request, shop, _ = initialize_test(rf, False) basket = get_basket(request) supplier = get_default_supplier() single_product_price = Decimal("50") single_product_min_price = Decimal("40") discount_amount_value = Decimal("200") # will exceed the minimum price quantity = 2 # the expected discount amount should not be greater than the products expected_discount_amount = basket.create_price(single_product_price) * quantity category = CategoryFactory() # create basket rule that requires 2 products in basket product = create_product(printable_gibberish(), shop=shop, supplier=supplier, default_price=single_product_price) shop_product = ShopProduct.objects.get(shop=shop, product=product) shop_product.minimum_price_value = single_product_min_price shop_product.save() shop_product.categories.add(category) basket.add_product(supplier=supplier, shop=shop, product=product, quantity=quantity) basket.shipping_method = get_shipping_method(shop=shop) basket.save() rule = ProductsInBasketCondition.objects.create(quantity=2) rule.products.add(product) rule.save() campaign = BasketCampaign.objects.create(active=True, shop=shop, name="test", public_name="test") campaign.conditions.add(rule) DiscountFromCategoryProducts.objects.create(campaign=campaign, discount_amount=discount_amount_value, category=category) assert rule.matches(basket, []) basket.uncache() # the discount amount should not exceed the minimum price. as the configued discount # will exceed, it should limit the discount amount final_lines = basket.get_final_lines() expected_discount_amount = basket.create_price((single_product_price - single_product_min_price) * quantity) original_price = basket.create_price(single_product_price) * quantity line = final_lines[0] assert line.discount_amount == expected_discount_amount assert basket.total_price == original_price - expected_discount_amount
def test_basket_free_product_coupon(rf): request, shop, _ = initialize_test(rf, False) basket = get_basket(request) supplier = get_default_supplier() single_product_price = "50" discount_amount_value = "10" # create basket rule that requires 2 products in basket product = create_product(printable_gibberish(), shop=shop, supplier=supplier, default_price=single_product_price) basket.add_product(supplier=supplier, shop=shop, product=product, quantity=1) basket.add_product(supplier=supplier, shop=shop, product=product, quantity=1) basket.shipping_method = get_shipping_method(shop=shop) basket.save() second_product = create_product(printable_gibberish(), shop=shop, supplier=supplier, default_price=single_product_price) rule = BasketTotalProductAmountCondition.objects.create(value="2") coupon = Coupon.objects.create(code="TEST", active=True) campaign = BasketCampaign.objects.create(active=True, shop=shop, name="test", public_name="test", coupon=coupon) campaign.conditions.add(rule) effect = FreeProductLine.objects.create(campaign=campaign) effect.products.add(second_product) basket.add_code(coupon.code) basket.uncache() final_lines = basket.get_final_lines() assert len(final_lines) == 3 line_types = [l.type for l in final_lines] assert OrderLineType.DISCOUNT not in line_types for line in basket.get_final_lines(): assert line.type in [OrderLineType.PRODUCT, OrderLineType.SHIPPING] if line.type == OrderLineType.SHIPPING: continue if line.product != product: assert line.product == second_product
def test_productdiscountamount_with_minimum_price(rf, per_line_discount): # Buy X amount of Y get Z discount from Y request, shop, _ = initialize_test(rf, False) basket = get_basket(request) supplier = get_default_supplier() single_product_price = Decimal("50") single_product_min_price = Decimal("40") discount_amount_value = Decimal("200") # will exceed the minimum price quantity = 2 # create basket rule that requires 2 products in basket product = create_product(printable_gibberish(), shop=shop, supplier=supplier, default_price=single_product_price) shop_product = ShopProduct.objects.get(product=product, shop=shop) shop_product.minimum_price_value = single_product_min_price shop_product.save() basket.add_product(supplier=supplier, shop=shop, product=product, quantity=quantity) basket.shipping_method = get_shipping_method(shop=shop) basket.save() rule = ProductsInBasketCondition.objects.create(quantity=2) rule.products.add(product) rule.save() campaign = BasketCampaign.objects.create(active=True, shop=shop, name="test", public_name="test") campaign.conditions.add(rule) effect = DiscountFromProduct.objects.create(campaign=campaign, discount_amount=discount_amount_value, per_line_discount=per_line_discount) effect.products.add(product) assert rule.matches(basket, []) basket.uncache() # the discount amount should not exceed the minimum price. as the configued discount # will exceed, it should limit the discount amount final_lines = basket.get_final_lines() expected_discount_amount = basket.create_price((single_product_price - single_product_min_price) * quantity) original_price = basket.create_price(single_product_price) * quantity line = final_lines[0] assert line.discount_amount == expected_discount_amount assert basket.total_price == original_price - expected_discount_amount
def test_basket_campaign_case2(rf): request, shop, group = initialize_test(rf, False) price = shop.create_price basket = get_basket(request) supplier = get_default_supplier() # create a basket rule that requires at least value of 200 rule = BasketTotalAmountCondition.objects.create(value="200") single_product_price = "50" discount_amount_value = "10" unique_shipping_method = get_shipping_method(shop, price=50) for x in range(3): product = create_product( printable_gibberish(), shop=shop, supplier=supplier, default_price=single_product_price) basket.add_product(supplier=supplier, shop=shop, product=product, quantity=1) assert basket.product_count == 3 campaign = BasketCampaign.objects.create( shop=shop, public_name="test", name="test", active=True) campaign.conditions.add(rule) campaign.save() BasketDiscountAmount.objects.create(discount_amount=discount_amount_value, campaign=campaign) assert len(basket.get_final_lines()) == 3 assert basket.total_price == price(single_product_price) * basket.product_count # check that shipping method affects campaign basket.shipping_method = unique_shipping_method basket.save() basket.uncache() assert len(basket.get_final_lines()) == 4 # Shipping should not affect the rule being triggered line_types = [l.type for l in basket.get_final_lines()] assert OrderLineType.DISCOUNT not in line_types product = create_product(printable_gibberish(), shop=shop, supplier=supplier, default_price=single_product_price) basket.add_product(supplier=supplier, shop=shop, product=product, quantity=1) assert len(basket.get_final_lines()) == 6 # Discount included assert OrderLineType.DISCOUNT in [l.type for l in basket.get_final_lines()]
def test_basket_dirtying_with_fnl(rf): shop = get_default_shop() supplier = get_default_supplier() product = create_product(printable_gibberish(), shop=shop, supplier=supplier, default_price=50) request = rf.get("/") request.session = {} request.shop = shop apply_request_middleware(request) basket = get_basket(request) line = basket.add_product( supplier=supplier, shop=shop, product=product, quantity=1, force_new_line=True, extra={"foo": "foo"} ) assert basket.dirty # The change should have dirtied the basket
def _init_basket_coupon_test(rf, code="TEST"): status = get_initial_order_status() request, shop, group = initialize_test(rf, False) basket = get_basket(request) supplier = get_default_supplier(shop) for x in range(2): product = create_product(printable_gibberish(), shop, supplier=supplier, default_price="50") basket.add_product(supplier=supplier, shop=shop, product=product, quantity=1) basket.shipping_method = get_shipping_method(shop=shop) # For shippable products dc = Coupon.objects.create(code=code, active=True) campaign = BasketCampaign.objects.create( shop=shop, name="test", public_name="test", coupon=dc, active=True ) BasketDiscountAmount.objects.create(discount_amount=shop.create_price("20"), campaign=campaign) rule = BasketTotalProductAmountCondition.objects.create(value=2) campaign.conditions.add(rule) campaign.save() return basket, dc, request, status
def _create_cart_with_products(rf, shop, user, product_count): person = get_person_contact(user) get_default_payment_method() get_default_shipping_method() request = rf.post("/", {"title": "test"}) request.shop = shop request.user = user request.person = person request.customer = person basket = get_basket(request) request = apply_request_middleware(request, user=user, person=person, customer=person, basket=basket) basket = _add_products_to_basket(basket, product_count) basket.save() return basket
def test_product_category_discount_percentage_greater_then_products(rf, include_tax): # Buy X amount of Y get Z discount from Y request, shop, _ = initialize_test(rf, include_tax) basket = get_basket(request) supplier = get_default_supplier(shop) single_product_price = "50" discount_percentage = Decimal(1.9) # 190% quantity = 2 # the expected discount amount should not be greater than the products expected_discount_amount = basket.create_price(single_product_price) * quantity category = CategoryFactory() # create basket rule that requires 2 products in basket product = create_product(printable_gibberish(), shop=shop, supplier=supplier, default_price=single_product_price) ShopProduct.objects.get(shop=shop, product=product).categories.add(category) basket.add_product(supplier=supplier, shop=shop, product=product, quantity=quantity) basket.shipping_method = get_shipping_method(shop=shop) basket.save() rule = ProductsInBasketCondition.objects.create(quantity=2) rule.products.add(product) rule.save() campaign = BasketCampaign.objects.create(active=True, shop=shop, name="test", public_name="test") campaign.conditions.add(rule) DiscountFromCategoryProducts.objects.create(campaign=campaign, discount_percentage=discount_percentage, category=category) assert rule.matches(basket, []) basket.uncache() final_lines = basket.get_final_lines() assert len(final_lines) == 2 # no new lines since the effect touches original lines original_price = basket.create_price(single_product_price) * quantity line = final_lines[0] assert line.discount_amount == expected_discount_amount assert basket.total_price == original_price - expected_discount_amount
def test_category_product_in_basket_condition(rf): request, shop, group = initialize_test(rf, False) basket = get_basket(request) supplier = get_default_supplier() category = get_default_category() product = create_product("The Product", shop=shop, default_price="200", supplier=supplier) basket.add_product(supplier=supplier, shop=shop, product=product, quantity=1) basket.shipping_method = get_shipping_method(shop=shop) shop_product = product.get_shop_instance(shop) assert category not in shop_product.categories.all() condition = CategoryProductsBasketCondition.objects.create( operator=ComparisonOperator.EQUALS, quantity=1) condition.categories.add(category) # No match the product does not have the category assert not condition.matches(basket, []) category.shop_products.add(shop_product) assert condition.matches(basket, []) basket.add_product(supplier=supplier, shop=shop, product=product, quantity=1) assert not condition.matches(basket, []) condition.operator = ComparisonOperator.GTE condition.save() assert condition.matches(basket, []) condition.excluded_categories.add(category) assert not condition.matches(basket, []) with pytest.raises(CampaignsInvalidInstanceForCacheUpdate): update_filter_cache("test", shop)
def test_basket_campaign_module_case1(rf): request, shop, group = initialize_test(rf, False) price = shop.create_price basket = get_basket(request) supplier = get_default_supplier() single_product_price = "50" discount_amount_value = "10" # create basket rule that requires 2 products in basket basket_rule1 = BasketTotalProductAmountCondition.objects.create(value="2") product = create_product(printable_gibberish(), shop=shop, supplier=supplier, default_price=single_product_price) basket.add_product(supplier=supplier, shop=shop, product=product, quantity=1) basket.shipping_method = get_shipping_method(shop=shop) basket.save() assert basket.product_count == 1 campaign = BasketCampaign.objects.create( shop=shop, public_name="test", name="test", active=True) campaign.conditions.add(basket_rule1) campaign.save() BasketDiscountAmount.objects.create(campaign=campaign, discount_amount=discount_amount_value) assert len(basket.get_final_lines()) == 2 # case 1 assert basket.total_price == price(single_product_price) # case 1 basket.add_product(supplier=supplier, shop=shop, product=product, quantity=1) basket.save() assert len(basket.get_final_lines()) == 3 # case 1 assert basket.product_count == 2 assert basket.total_price == (price(single_product_price) * basket.product_count - price(discount_amount_value)) assert OrderLineType.DISCOUNT in [l.type for l in basket.get_final_lines()] # Make sure disabling campaign disables it conditions assert campaign.conditions.filter(active=True).exists() campaign.active = False campaign.save() assert not campaign.conditions.filter(active=True).exists()
def _save_cart_with_products(rf, user): shop = get_default_shop() person = get_person_contact(user) get_default_payment_method() get_default_shipping_method() request = rf.post("/", {"title": "test"}) request.shop = shop request.user = user request.person = person request.customer = person basket = get_basket(request) request = apply_request_middleware(request, user=user, person=person, customer=person, basket=basket) basket = _add_products_to_basket(basket) basket.save() response = CartSaveView.as_view()(request) basket = StoredBasket.objects.filter(title="test").first() data = json.loads(response.content.decode("utf8")) assert data["ok"], "cart saved successfully" return basket
def test_product_in_basket_condition(rf): request, shop, group = initialize_test(rf, False) basket = get_basket(request) supplier = get_default_supplier() product = create_product("Just-A-Product-Too", shop, default_price="200", supplier=supplier) basket.add_product(supplier=supplier, shop=shop, product=product, quantity=1) condition = ProductsInBasketCondition.objects.create() condition.values = [product] condition.save() assert condition.values.first() == product assert condition.matches(basket, []) condition.quantity = 2 condition.save() assert not condition.matches(basket, []) basket.add_product(supplier=supplier, shop=shop, product=product, quantity=1) assert condition.matches(basket, []) condition.operator = ComparisonOperator.EQUALS condition.save() assert condition.matches(basket, []) basket.add_product(supplier=supplier, shop=shop, product=product, quantity=1) assert not condition.matches(basket, [])
def test_campaign_with_coupons2(rf): basket, dc, request, status = _init_basket_coupon_test(rf, code="tEsT") assert len(basket.get_final_lines() ) == 3 # no discount was applied because coupon is required customer_code = "Test" # Customer typoed the code, should still match basket.add_code(customer_code) assert customer_code in basket.codes assert len(basket.codes) == 1 # only one code basket.add_code(customer_code.upper()) assert customer_code.upper() not in basket.codes assert len(basket.codes) == 1 # only one code assert len(basket.get_final_lines() ) == 4 # now basket has codes so they will be applied too assert OrderLineType.DISCOUNT in [l.type for l in basket.get_final_lines()] # Ensure codes persist between requests, so do what the middleware would, i.e. basket.save() # and then reload the basket: del request.basket basket = get_basket(request) assert basket.codes != [dc.code] # they don't match like this assert [c.upper() for c in basket.codes] == [dc.code.upper()] # they match like this assert [c.upper() for c in basket.codes ] != [customer_code] # they don't match like this assert basket.codes == [customer_code] # they match like this assert len(basket.get_final_lines() ) == 4 # now basket has codes so they will be applied too assert OrderLineType.DISCOUNT in [l.type for l in basket.get_final_lines()] basket.status = status creator = OrderCreator(request) order = creator.create_order(basket) assert CouponUsage.objects.filter(order=order).count() == 1 assert CouponUsage.objects.filter(order=order, coupon__code=dc.code).count() == 1
def test_multiple_campaigns_match_with_coupon(rf): request, shop, group = initialize_test(rf, False) price = shop.create_price basket = get_basket(request) supplier = get_default_supplier() # create a basket rule that requires atleast value of 200 rule = BasketTotalAmountCondition.objects.create(value="200") product_price = "200" discount1 = "10" discount2 = "20" product = create_product(printable_gibberish(), shop=shop, supplier=supplier, default_price=product_price) basket.add_product(supplier=supplier, shop=shop, product=product, quantity=1) basket.shipping_method = get_shipping_method(shop=shop) campaign = BasketCampaign.objects.create(shop=shop, public_name="test", name="test", active=True) campaign.conditions.add(rule) campaign.save() BasketDiscountAmount.objects.create(discount_amount=discount1, campaign=campaign) dc = Coupon.objects.create(code="TEST", active=True) campaign2 = BasketCampaign.objects.create( shop=shop, public_name="test", name="test", coupon=dc, active=True ) BasketDiscountAmount.objects.create(discount_amount=discount2, campaign=campaign2) basket.add_product(supplier=supplier, shop=shop, product=product, quantity=1) resp = handle_add_campaign_code(request, basket, dc.code) assert resp.get("ok") discount_lines_values = [line.discount_amount for line in basket.get_final_lines()] assert price(discount1) in discount_lines_values assert price(discount2) in discount_lines_values assert basket.total_price == (price(product_price) * basket.product_count - price(discount1) - price(discount2))
def test_method_phase_basic(rf, admin_user, get_method, data, method_id): activate("en") shop = get_default_shop() method = get_method(shop, price=0, name=data["name"]) method.description = data["description"] method.save() assert method.enabled view = MethodsOnlyCheckoutView.as_view() # To make method visible, basket must be available person = get_person_contact(admin_user) request = apply_request_middleware(rf.get("/")) request.shop = shop request.user = admin_user request.person = person request.customer = person basket = get_basket(request) # add product to bakset supplier = get_default_supplier() product = create_product(printable_gibberish(), shop=shop, supplier=supplier, default_price=50) basket.add_product(supplier=supplier, shop=shop, product=product, quantity=2) assert basket.product_count, "basket has products" basket.save() request = apply_request_middleware(request, user=admin_user, person=person, customer=person, basket=basket) # request = apply_request_middleware(rf.get("/")) response = view(request=request, phase='methods') if hasattr(response, "render"): response.render() assert response.status_code in [200, 302] soup = BeautifulSoup(response.content) method_soup = soup.find("div", {"id": method_id}) assert data["name"] in method_soup.text assert data["description"] in method_soup.text assert soup.find("input", {"id": "%s_%s" % (method_id, method.pk)})
def test_productfilter_works(): rf = RequestFactory() request, shop, group = initialize_test(rf, False) price = shop.create_price product_price = "100" discount_percentage = "0.30" supplier = get_default_supplier() product = create_product(printable_gibberish(), shop=shop, supplier=supplier, default_price=product_price) shop_product = product.get_shop_instance(shop) # create catalog campaign catalog_filter = ProductFilter.objects.create() catalog_filter.products.add(product) assert catalog_filter.matches(shop_product) catalog_campaign = CatalogCampaign.objects.create(shop=shop, active=True, name="test") catalog_campaign.filters.add(catalog_filter) cdp = ProductDiscountPercentage.objects.create( campaign=catalog_campaign, discount_percentage=discount_percentage) # add product to basket basket = get_basket(request) basket.add_product(supplier=supplier, shop=shop, product=product, quantity=1) basket.shipping_method = get_shipping_method(shop=shop) basket.save() expected_total = price(product_price) - (Decimal(discount_percentage) * price(product_price)) assert basket.total_price == expected_total
def test_percentage_campaign(rf): request, shop, group = initialize_test(rf, False) price = shop.create_price basket = get_basket(request) supplier = get_default_supplier(shop) # create a basket rule that requires at least value of 200 rule = BasketTotalAmountCondition.objects.create(value="200") product_price = "200" discount_percentage = "0.1" expected_discounted_price = price(product_price) - ( price(product_price) * Decimal(discount_percentage)) product = create_product(printable_gibberish(), shop=shop, supplier=supplier, default_price=product_price) basket.add_product(supplier=supplier, shop=shop, product=product, quantity=1) basket.shipping_method = get_shipping_method(shop=shop) campaign = BasketCampaign.objects.create(shop=shop, public_name="test", name="test", active=True) campaign.conditions.add(rule) campaign.save() BasketDiscountPercentage.objects.create( campaign=campaign, discount_percentage=discount_percentage) assert len(basket.get_final_lines()) == 3 assert basket.product_count == 1 assert basket.total_price == expected_discounted_price
def test_productdiscountamount(rf): # Buy X amount of Y get Z discount from Y request, shop, _ = initialize_test(rf, False) basket = get_basket(request) supplier = get_default_supplier(shop) single_product_price = "50" discount_amount_value = "10" quantity = 2 # create basket rule that requires 2 products in basket product = create_product(printable_gibberish(), shop=shop, supplier=supplier, default_price=single_product_price) basket.add_product(supplier=supplier, shop=shop, product=product, quantity=quantity) basket.shipping_method = get_shipping_method(shop=shop) basket.save() rule = ProductsInBasketCondition.objects.create(quantity=2) rule.products.add(product) rule.save() campaign = BasketCampaign.objects.create(active=True, shop=shop, name="test", public_name="test") campaign.conditions.add(rule) effect = DiscountFromProduct.objects.create( campaign=campaign, discount_amount=discount_amount_value) effect.products.add(product) assert rule.matches(basket, []) basket.uncache() final_lines = basket.get_final_lines() assert len(final_lines) == 2 # no new lines since the effect touches original lines expected_discount_amount = basket.create_price(discount_amount_value) original_price = basket.create_price(single_product_price) * quantity line = final_lines[0] assert line.discount_amount == expected_discount_amount assert basket.total_price == original_price - expected_discount_amount
def test_category_products_effect_with_amount(rf): request, shop, group = initialize_test(rf, False) basket = get_basket(request) category = get_default_category() supplier = get_default_supplier() single_product_price = "50" discount_amount_value = "10" quantity = 5 product = create_product("The product", shop=shop, supplier=supplier, default_price=single_product_price) shop_product = product.get_shop_instance(shop) shop_product.categories.add(category) basket.add_product(supplier=supplier, shop=shop, product=product, quantity=quantity) basket.shipping_method = get_shipping_method(shop=shop) basket.save() rule = CategoryProductsBasketCondition.objects.create(operator=ComparisonOperator.EQUALS, quantity=quantity) rule.categories.add(category) campaign = BasketCampaign.objects.create(active=True, shop=shop, name="test", public_name="test") campaign.conditions.add(rule) DiscountFromCategoryProducts.objects.create( campaign=campaign, category=category, discount_amount=discount_amount_value) assert rule.matches(basket, []) basket.uncache() final_lines = basket.get_final_lines() assert len(final_lines) == 2 # no new lines since the effect touches original lines expected_discount_amount = quantity * basket.create_price(discount_amount_value) original_price = basket.create_price(single_product_price) * quantity line = final_lines[0] assert line.discount_amount == expected_discount_amount assert basket.total_price == original_price - expected_discount_amount
def create_basket_and_campaign(request, conditions, product_price_value, campaign_discount_value): product = create_product( "Some crazy product", request.shop, get_default_supplier(), default_price=product_price_value) basket = get_basket(request) basket.customer = request.customer supplier = get_default_supplier() basket.add_product(supplier=supplier, shop=request.shop, product=product, quantity=1) basket.shipping_method = get_shipping_method(shop=request.shop) original_line_count = len(basket.get_final_lines()) assert original_line_count == 2 assert basket.product_count == 1 original_price = basket.total_price campaign = BasketCampaign.objects.create( shop=request.shop, name="test", public_name="test", active=True) BasketDiscountAmount.objects.create(campaign=campaign, discount_amount=campaign_discount_value) for condition in conditions: campaign.conditions.add(condition) assert campaign.is_available() return basket, original_line_count, original_price
def test_basket_orderability_change(rf): StoredBasket.objects.all().delete() shop = get_default_shop() supplier = get_default_supplier() product = create_product(printable_gibberish(), shop=shop, supplier=supplier, default_price=50) request = rf.get("/") request.session = {} request.shop = shop apply_request_middleware(request) basket = get_basket(request) line = basket.add_product(supplier=supplier, shop=shop, product=product, quantity=1, force_new_line=True, extra={"foo": "foo"}) assert len(basket.get_lines()) == 1 product.soft_delete() assert basket.dirty assert len(basket.get_lines()) == 0
def _get_order_with_coupon(request, initial_status, condition_product_count=1): shop = request.shop basket = get_basket(request) supplier = get_default_supplier() product = create_product(printable_gibberish(), shop=shop, supplier=supplier, default_price="50") basket.add_product(supplier=supplier, shop=shop, product=product, quantity=1) basket.shipping_method = get_shipping_method( shop=shop) # For shippable products dc = Coupon.objects.create(code="TEST", active=True) campaign = BasketCampaign.objects.create(shop=shop, name="test", public_name="test", coupon=dc, active=True) BasketDiscountAmount.objects.create( discount_amount=shop.create_price("20"), campaign=campaign) rule = BasketTotalProductAmountCondition.objects.create(value=1) campaign.conditions.add(rule) campaign.save() basket.add_code(dc.code) basket.save() basket.status = initial_status creator = OrderCreator(request) order = creator.create_order(basket) assert order.lines.count() == 3 assert OrderLineType.DISCOUNT in [l.type for l in order.lines.all()] return order
def test_basket_shipping_error(rf): StoredBasket.objects.all().delete() shop = get_default_shop() supplier = get_default_supplier() shipped_product = create_product(printable_gibberish(), shop=shop, supplier=supplier, default_price=50, shipping_mode=ShippingMode.SHIPPED) unshipped_product = create_product(printable_gibberish(), shop=shop, supplier=supplier, default_price=50, shipping_mode=ShippingMode.NOT_SHIPPED) request = rf.get("/") request.session = {} request.shop = shop apply_request_middleware(request) basket = get_basket(request) # With a shipped product but no shipping methods, we oughta get an error basket.add_product(supplier=supplier, shop=shop, product=shipped_product, quantity=1) assert any(ve.code == "no_common_shipping" for ve in basket.get_validation_errors()) basket.clear_all() # But with an unshipped product, we should not basket.add_product(supplier=supplier, shop=shop, product=unshipped_product, quantity=1) assert not any(ve.code == "no_common_shipping" for ve in basket.get_validation_errors())
def test_percentage_campaign_full_discount(rf, include_tax): request, shop, group = initialize_test(rf, include_tax) create_default_order_statuses() tax = get_tax("sales-tax", "Sales Tax", Decimal(0.2)) # 20% create_default_tax_rule(tax) basket = get_basket(request) supplier = get_default_supplier(shop) product = create_product(printable_gibberish(), shop=shop, supplier=supplier, default_price=200) basket.add_product(supplier=supplier, shop=shop, product=product, quantity=1) basket.shipping_method = get_shipping_method(shop=shop) basket.status = get_initial_order_status() campaign = BasketCampaign.objects.create(shop=shop, public_name="test", name="test", active=True) # 100% of discount BasketDiscountPercentage.objects.create(campaign=campaign, discount_percentage=Decimal(1)) assert len(basket.get_final_lines()) == 3 assert basket.product_count == 1 assert basket.total_price.value == Decimal() order_creator = OrderCreator() order = order_creator.create_order(basket) order.create_payment(order.taxful_total_price) assert order.taxful_total_price.value == Decimal()
def test_product_in_basket_condition_with_variation_parent(rf): request, shop, group = initialize_test(rf, False) basket = get_basket(request) supplier = get_default_supplier() product = create_product("test-product", shop, default_price="200", supplier=supplier, mode=ProductMode.SIMPLE_VARIATION_PARENT) child_products = [] for x in range(0, 3): child_product = create_product("test-product-%s" % x, shop, default_price="10", supplier=supplier) child_product.link_to_parent(product) child_products.append(child_product) condition = ProductsInBasketCondition.objects.create() condition.values = [product] condition.operator = ComparisonOperator.EQUALS condition.quantity = 3 condition.save() assert not condition.matches(basket, []) for child_product in child_products: basket.add_product(supplier=supplier, shop=shop, product=child_product, quantity=1) assert condition.matches(basket, [])
def test_basket_clearing(rf): StoredBasket.objects.all().delete() shop = get_default_shop() supplier = get_default_supplier() product = create_product(printable_gibberish(), shop=shop, supplier=supplier, default_price=50) request = rf.get("/") request.session = {} request.shop = shop apply_request_middleware(request) basket = get_basket(request) pm = get_default_payment_method() sm = get_default_shipping_method() basket.shipping_method = sm basket.payment_method = pm basket.save() assert basket.shipping_method assert basket.payment_method basket.clear_all() assert not basket.shipping_method assert not basket.payment_method
def test_basket_total_value_conditions(rf): request, shop, group = initialize_test(rf, False) basket = get_basket(request) supplier = get_default_supplier() product = create_product("Just-A-Product-Too", shop, default_price="200", supplier=supplier) basket.add_product(supplier=supplier, shop=shop, product=product, quantity=1) condition = BasketTotalProductAmountCondition.objects.create() condition.value = 1 condition.save() assert condition.value == 1 assert condition.matches(basket, []) assert "basket has at least the product count entered here" in force_text(condition.description) condition2 = BasketMaxTotalProductAmountCondition.objects.create() condition2.value = 1 condition2.save() assert condition2.matches(basket, []) basket.add_product(supplier=supplier, shop=shop, product=product, quantity=1) assert not condition2.matches(basket, [])
def _set_basket(self, request): request.basket = get_basket(request)
def test_basket_campaign_with_multiple_supppliers(rf): request, shop, group = initialize_test(rf, False) supplier1 = Supplier.objects.create(identifier="1") supplier2 = Supplier.objects.create(identifier="2") supplier1.shops.add(shop) supplier2.shops.add(shop) price = shop.create_price basket = get_basket(request) single_product_price = "50" discount_amount_supplier1 = "10" discount_amount_supplier2 = "40" product1 = create_product("product1", shop=shop, supplier=supplier1, default_price=single_product_price) product2 = create_product("product2", shop=shop, supplier=supplier2, default_price=single_product_price) basket.add_product(supplier=supplier1, shop=shop, product=product1, quantity=1) basket.add_product(supplier=supplier2, shop=shop, product=product2, quantity=1) basket.shipping_method = get_shipping_method(shop=shop) basket.save() assert basket.product_count == 2 assert basket.total_price.value == 100 # Create campaign for supplier one basket_rule1 = ProductsInBasketCondition.objects.create(quantity=1) basket_rule1.products.add(product1) campaign = BasketCampaign.objects.create(shop=shop, public_name="test", name="test", active=True, supplier=supplier1) campaign.conditions.add(basket_rule1) campaign.save() BasketDiscountAmount.objects.create( campaign=campaign, discount_amount=discount_amount_supplier1) basket.uncache() lines = basket.get_final_lines() assert len(lines) == 4 assert basket.total_price.value == 90 # 10d discount from the supplier1 product line = _get_discount_line(lines, 10) assert line.supplier == supplier1 # Create campaign for supplier two basket_rule2 = ProductsInBasketCondition.objects.create(quantity=1) basket_rule2.products.add(product2) campaign = BasketCampaign.objects.create(shop=shop, public_name="test", name="test", active=True, supplier=supplier2) campaign.conditions.add(basket_rule2) campaign.save() BasketDiscountAmount.objects.create( campaign=campaign, discount_amount=discount_amount_supplier2) basket.uncache() lines = basket.get_final_lines() assert len(lines) == 5 assert basket.total_price.value == 50 # -10d - 40d from 100d line = _get_discount_line(lines, 40) assert line.supplier == supplier2
def test_basket_campaign_with_multiple_supppliers_sharing_product(rf): request, shop, group = initialize_test(rf, False) supplier1 = Supplier.objects.create(identifier="1") supplier2 = Supplier.objects.create(identifier="2") supplier1.shops.add(shop) supplier2.shops.add(shop) price = shop.create_price basket = get_basket(request) single_product_price = "50" discount_amount = "10" product = create_product("product1", shop=shop, supplier=supplier1, default_price=single_product_price) shop_product = product.get_shop_instance(shop) shop_product.suppliers.add(supplier2) basket.add_product(supplier=supplier1, shop=shop, product=product, quantity=1) basket.add_product(supplier=supplier2, shop=shop, product=product, quantity=1) basket.shipping_method = get_shipping_method(shop=shop) basket.save() assert basket.product_count == 2 assert basket.total_price.value == 100 # Create campaign for supplier one basket_rule = ProductsInBasketCondition.objects.create(quantity=2) basket_rule.products.add(product) campaign = BasketCampaign.objects.create(shop=shop, public_name="test", name="test", active=True, supplier=supplier1) campaign.conditions.add(basket_rule) campaign.save() BasketDiscountAmount.objects.create(campaign=campaign, discount_amount=discount_amount) basket.uncache() assert len(basket.get_final_lines()) == 3 # +1 for shipping line # No disocunt since not enough products in basket for supplier 1 assert basket.total_price.value == 100 # Let's add second product for supplier 2 # We still should not get discount basket.add_product(supplier=supplier2, shop=shop, product=product, quantity=1) basket.save() basket.uncache() assert len(basket.get_final_lines()) == 3 # No disocunt since not enough products in basket for supplier 1 assert basket.total_price.value == 150 # Let's add second produdct for supplier one and now we should # finally get that 10d discount basket.add_product(supplier=supplier1, shop=shop, product=product, quantity=1) basket.save() basket.uncache() assert len(basket.get_final_lines()) == 4 # +1 for discount line # No disocunt since not enough products in basket for supplier 1 assert basket.total_price.value == 190 # For sake of it lets do second discount for supplier 2 after 3 products discount_amount_2 = "40" # Create campaign for supplier two basket_rule2 = ProductsInBasketCondition.objects.create(quantity=3) basket_rule2.products.add(product) campaign = BasketCampaign.objects.create(shop=shop, public_name="test", name="test", active=True, supplier=supplier2) campaign.conditions.add(basket_rule2) campaign.save() BasketDiscountAmount.objects.create(campaign=campaign, discount_amount=discount_amount_2) assert len(basket.get_final_lines()) == 4 # No disocunt since not enough products in basket for supplier 1 assert basket.total_price.value == 190 # Let's add third produdct for supplier two and now we should # finally get that 10d discount basket.add_product(supplier=supplier2, shop=shop, product=product, quantity=1) basket.save() basket.uncache() assert len(basket.get_final_lines()) == 5 # +1 for discount line # No disocunt since not enough products in basket for supplier 1 assert basket.total_price.value == 200
def test_undiscounted_effects(rf, include_tax): request, shop, _ = initialize_test(rf, include_tax) basket = get_basket(request) supplier = get_default_supplier(shop) single_product_price = Decimal(50) discounted_product_quantity = 4 normal_priced_product_quantity = 2 discount_percentage = Decimal(0.2) # 20% discount_amount = basket.create_price(single_product_price * normal_priced_product_quantity * discount_percentage) category = CategoryFactory() discounted_product = create_product(printable_gibberish(), shop=shop, supplier=supplier, default_price=single_product_price) second_product = create_product(printable_gibberish(), shop=shop, supplier=supplier, default_price=single_product_price) ShopProduct.objects.get( shop=shop, product=discounted_product).categories.add(category) ShopProduct.objects.get(shop=shop, product=second_product).categories.add(category) basket.add_product(supplier=supplier, shop=shop, product=discounted_product, quantity=discounted_product_quantity) basket.add_product(supplier=supplier, shop=shop, product=second_product, quantity=normal_priced_product_quantity) basket.shipping_method = get_shipping_method(shop=shop) basket.save() # Store basket price before any campaigns exists original_price = basket.total_price # CatalogCampaign catalog_campaign = CatalogCampaign.objects.create(active=True, shop=shop, name="test", public_name="test") # Limit catalog campaign to "discounted_product" product_filter = ProductFilter.objects.create() product_filter.products.add(discounted_product) catalog_campaign.filters.add(product_filter) # BasketCampaign campaign = BasketCampaign.objects.create(active=True, shop=shop, name="test2", public_name="test2") final_lines = basket.get_final_lines() assert len(final_lines) == 3 # Discount based on undiscounted product values DiscountPercentageFromUndiscounted.objects.create( campaign=campaign, discount_percentage=discount_percentage) basket.uncache() final_lines = basket.get_final_lines() assert len(final_lines) == 4 discounted_basket_price = original_price - discount_amount assert basket.total_price.as_rounded( ) == discounted_basket_price.as_rounded()