Example #1
0
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
Example #2
0
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()
Example #3
0
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")
Example #4
0
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
Example #5
0
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())
Example #6
0
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
Example #7
0
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, [])
Example #17
0
    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, [])
Example #19
0
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
Example #20
0
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
Example #21
0
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
Example #22
0
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
Example #23
0
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
Example #24
0
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
Example #25
0
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
Example #27
0
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()
Example #28
0
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)
Example #29
0
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
Example #30
0
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
Example #31
0
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()]
Example #33
0
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
Example #34
0
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
Example #35
0
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
Example #36
0
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)
Example #38
0
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()
Example #39
0
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, [])
Example #41
0
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
Example #42
0
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))
Example #43
0
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)})
Example #44
0
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
Example #45
0
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
Example #46
0
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
Example #49
0
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
Example #51
0
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())
Example #52
0
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, [])
Example #53
0
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, [])
Example #55
0
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, [])
Example #57
0
 def _set_basket(self, request):
     request.basket = get_basket(request)
Example #58
0
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
Example #59
0
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
Example #60
0
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()