def seed_source(user, shop=None): source_shop = shop or get_default_shop() source = BasketishOrderSource(source_shop) billing_address = get_address() shipping_address = get_address(name="Shippy Doge") source.status = get_initial_order_status() source.billing_address = billing_address source.shipping_address = shipping_address source.customer = get_person_contact(user) source.payment_method = get_payment_method(shop) source.shipping_method = get_shipping_method(shop) assert source.payment_method_id == get_payment_method(shop).id assert source.shipping_method_id == get_shipping_method(shop).id return source
def _get_frontend_order_state(shop, contact): tax = Tax.objects.create(code="test_code", rate=decimal.Decimal("0.20"), name="Default") tax_class = TaxClass.objects.create(identifier="test_tax_class", name="Default") rule = TaxRule.objects.create(tax=tax) rule.tax_classes.add(tax_class) rule.save() product = create_product(sku=printable_gibberish(), supplier=get_default_supplier(), shop=shop) product.tax_class = tax_class product.save() lines = [{"id": "x", "type": "product", "product": {"id": product.id}, "quantity": "32", "baseUnitPrice": 50}] state = { "customer": { "id": contact.id if contact else None, "billingAddress": _encode_address(contact.default_billing_address) if contact else {}, "shippingAddress": _encode_address(contact.default_shipping_address) if contact else {}, }, "lines": lines, "methods": { "shippingMethod": {"id": get_shipping_method(shop=shop).id}, "paymentMethod": {"id": get_payment_method(shop=shop).id}, }, "shop": { "selected": { "id": shop.id, "name": shop.safe_translation_getter("name"), "currency": shop.currency, "priceIncludeTaxes": shop.prices_include_tax, } }, } return state
def test_translations_of_method_and_component(): sm = get_shipping_method(name="Unique shipping") sm.set_current_language('en') sm.name = "Shipping" sm.set_current_language('fi') sm.name = "Toimitus" sm.save() cost = FixedCostBehaviorComponent.objects.language('fi').create( price_value=10, description="kymppi") cost.set_current_language('en') cost.description = "ten bucks" cost.save() sm.behavior_components.add(cost) source = BasketishOrderSource(get_default_shop()) source.shipping_method = sm translation.activate('fi') shipping_lines = [ line for line in source.get_final_lines() if line.type == OrderLineType.SHIPPING] assert len(shipping_lines) == 1 assert shipping_lines[0].text == 'Toimitus: kymppi' translation.activate('en') source.uncache() shipping_lines = [ line for line in source.get_final_lines() if line.type == OrderLineType.SHIPPING] assert len(shipping_lines) == 1 assert shipping_lines[0].text == 'Shipping: ten bucks'
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_limited_methods(): """ Test that products can declare that they limit available shipping methods. """ unique_shipping_method = get_shipping_method(name="unique", price=0) shop = get_default_shop() common_product = create_product(sku="SH_COMMON", shop=shop) # A product that does not limit shipping methods unique_product = create_product(sku="SH_UNIQUE", shop=shop) # A product that only supports unique_shipping_method unique_shop_product = unique_product.get_shop_instance(shop) unique_shop_product.limit_shipping_methods = True unique_shop_product.shipping_methods.add(unique_shipping_method) unique_shop_product.save() impossible_product = create_product(sku="SH_IMP", shop=shop) # A product that can't be shipped at all imp_shop_product = impossible_product.get_shop_instance(shop) imp_shop_product.limit_shipping_methods = True imp_shop_product.save() for product_ids, method_ids in [ ((common_product.pk, unique_product.pk), (unique_shipping_method.pk,)), ((common_product.pk,), ShippingMethod.objects.values_list("pk", flat=True)), ((unique_product.pk,), (unique_shipping_method.pk,)), ((unique_product.pk, impossible_product.pk,), ()), ((common_product.pk, impossible_product.pk,), ()), ]: product_ids = set(product_ids) assert ShippingMethod.objects.available_ids(shop=shop, products=product_ids) == set(method_ids)
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, []) 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, []) condition.excluded_categories.add(category) assert not condition.matches(basket, [])
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) basket.shipping_method = get_shipping_method(shop=shop) 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()) == 3 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_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) 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_source_lines_with_multiple_fixed_costs(): """ Costs with description creates new line always and costs without description is combined into one line. """ translation.activate("en") starting_price_value = 5 sm = get_shipping_method(name="Multiple costs", price=starting_price_value) sm.behavior_components.clear() source = BasketishOrderSource(get_default_shop()) source.shipping_method = sm lines = list(sm.get_lines(source)) assert len(lines) == 1 assert get_total_price_value(lines) == Decimal("0") sm.behavior_components.add(FixedCostBehaviorComponent.objects.create(price_value=10)) lines = list(sm.get_lines(source)) assert len(lines) == 1 assert get_total_price_value(lines) == Decimal("10") sm.behavior_components.add(FixedCostBehaviorComponent.objects.create(price_value=15, description="extra")) lines = list(sm.get_lines(source)) assert len(lines) == 2 assert get_total_price_value(lines) == Decimal("25") sm.behavior_components.add(FixedCostBehaviorComponent.objects.create(price_value=1)) lines = list(sm.get_lines(source)) assert len(lines) == 2 assert get_total_price_value(lines) == Decimal("26")
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 _get_source(user, shipping_country, billing_country): prices_include_taxes = True shop = get_shop(prices_include_taxes) payment_method = get_payment_method(shop) shipping_method = get_shipping_method(shop) source = _seed_source(shop, user, shipping_country, billing_country) source.payment_method = payment_method source.shipping_method = shipping_method assert source.payment_method_id == payment_method.id assert source.shipping_method_id == shipping_method.id supplier = get_default_supplier() product = create_product( sku="test-%s--%s" % (prices_include_taxes, 10), shop=source.shop, supplier=supplier, default_price=10 ) source.add_line( type=OrderLineType.PRODUCT, product=product, supplier=supplier, quantity=1, base_unit_price=source.create_price(10), ) assert payment_method == source.payment_method assert shipping_method == source.shipping_method return source
def _get_source(user, prices_include_taxes, total_price_value): shop = get_shop(prices_include_taxes) payment_method = get_payment_method(shop) shipping_method = get_shipping_method(shop) source = _seed_source(shop, user) source.payment_method = payment_method source.shipping_method = shipping_method assert source.payment_method_id == payment_method.id assert source.shipping_method_id == shipping_method.id supplier = get_default_supplier() product = create_product( sku="test-%s--%s" % (prices_include_taxes, total_price_value), shop=source.shop, supplier=supplier, default_price=total_price_value ) source.add_line( type=OrderLineType.PRODUCT, product=product, supplier=supplier, quantity=1, base_unit_price=source.create_price(total_price_value), ) if prices_include_taxes: assert source.taxful_total_price.value == total_price_value else: assert source.taxless_total_price.value == total_price_value assert payment_method == source.payment_method assert shipping_method == source.shipping_method return source, shipping_method
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_basket_free_product_coupon(rf): request, shop, _ = initialize_test(rf, False) basket = get_basket(request) supplier = get_default_supplier() single_product_price = "50" # 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_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_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_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(shop) 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_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 create_service(shop, line, tax_classes): assert line.quantity == 1 and line.discount == 0 if line.is_payment: meth = get_payment_method(shop=shop, price=line.price, name=line.payment_name) elif line.is_shipping: meth = get_shipping_method(shop=shop, price=line.price, name=line.shipping_name) meth.tax_class = tax_classes[line.tax_name] meth.save() return meth
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(shop) 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 test_waiver(): sm = get_shipping_method(name="Waivey", price=100, waive_at=370) source = BasketishOrderSource(get_default_shop()) assert sm.get_effective_name(source) == u"Waivey" assert sm.get_total_cost(source).price == source.create_price(100) source.add_line( type=OrderLineType.PRODUCT, product=get_default_product(), base_unit_price=source.create_price(400), quantity=1 ) assert sm.get_total_cost(source).price == source.create_price(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
def _get_frontend_order_state(shop, contact): tax = Tax.objects.create(code="test_code", rate=decimal.Decimal("0.20"), name="Default") tax_class = TaxClass.objects.create(identifier="test_tax_class", name="Default") rule = TaxRule.objects.create(tax=tax) rule.tax_classes.add(tax_class) rule.save() supplier = get_default_supplier(shop) product = create_product(sku=printable_gibberish(), supplier=supplier, shop=shop) product.tax_class = tax_class product.save() lines = [{ "id": "x", "type": "product", "product": { "id": product.id }, "quantity": "32", "baseUnitPrice": 50, "supplier": { "name": supplier.name, "id": supplier.id }, }] state = { "customer": { "id": contact.id if contact else None, "billingAddress": _encode_address(contact.default_billing_address) if contact else {}, "shippingAddress": _encode_address( contact.default_shipping_address) if contact else {}, }, "lines": lines, "methods": { "shippingMethod": { "id": get_shipping_method(shop=shop).id }, "paymentMethod": { "id": get_payment_method(shop=shop).id }, }, "shop": { "selected": { "id": shop.id, "name": shop.safe_translation_getter("name"), "currency": shop.currency, "priceIncludeTaxes": shop.prices_include_tax, } }, } return state
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(shop) 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_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_basket_free_product(rf): request, shop, _ = initialize_test(rf, False) basket = get_basket(request) supplier = get_default_supplier(shop) 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 create_service(shop, line, tax_classes): assert line.quantity == 1 and line.discount == 0 if line.is_payment: meth = get_payment_method( shop=shop, price=line.price, name=line.payment_name) elif line.is_shipping: meth = get_shipping_method( shop=shop, price=line.price, name=line.shipping_name) meth.tax_class = tax_classes[line.tax_name] meth.save() return meth
def _init_test_for_product_with_basket(rf, default_price): request, product = _init_test_for_product_without_basket(rf, default_price) shop = factories.get_default_shop() supplier = factories.get_default_supplier() basket = get_basket(request) basket.status = factories.get_initial_order_status() basket.add_product(supplier=supplier, shop=shop, product=product, quantity=1) basket.shipping_method = factories.get_shipping_method(shop=shop) basket.payment_method = factories.get_payment_method(shop=shop) assert basket.shop == request.shop assert basket.customer == request.customer return request, product, basket
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()
def test_fixed_cost_with_waiving_costs(): sm = get_shipping_method(name="Fixed and waiving", price=5) sm.behavior_components.add(*[ WaivingCostBehaviorComponent.objects.create(price_value=p, waive_limit_value=w) for (p, w) in [(3, 5), (7, 10), (10, 30)] ]) source = BasketishOrderSource(get_default_shop()) source.shipping_method = sm def pricestr(pi): assert pi.price.unit_matches_with(source.create_price(0)) return "%.0f EUR (%.0f EUR)" % (pi.price.value, pi.base_price.value) assert pricestr(sm.get_total_cost(source)) == "25 EUR (25 EUR)" assert source.total_price.value == 25 source.add_line(type=OrderLineType.PRODUCT, product=get_default_product(), base_unit_price=source.create_price(2), quantity=1) assert pricestr(sm.get_total_cost(source)) == "25 EUR (25 EUR)" assert source.total_price.value == 27 source.add_line(type=OrderLineType.PRODUCT, product=get_default_product(), base_unit_price=source.create_price(3), quantity=1) assert pricestr(sm.get_total_cost(source)) == "22 EUR (25 EUR)" assert source.total_price.value == 27 source.add_line(type=OrderLineType.PRODUCT, product=get_default_product(), base_unit_price=source.create_price(10), quantity=1) assert pricestr(sm.get_total_cost(source)) == "15 EUR (25 EUR)" assert source.total_price.value == 30 source.add_line(type=OrderLineType.PRODUCT, product=get_default_product(), base_unit_price=source.create_price(10), quantity=1) assert pricestr(sm.get_total_cost(source)) == "15 EUR (25 EUR)" assert source.total_price.value == 40 source.add_line(type=OrderLineType.PRODUCT, product=get_default_product(), base_unit_price=source.create_price(10), quantity=1) assert pricestr(sm.get_total_cost(source)) == "5 EUR (25 EUR)" assert source.total_price.value == 40
def test_percentage_campaign_different_coupon_supplier(rf): request, shop, group = initialize_test(rf, True) create_default_order_statuses() basket = get_basket(request) supplier = get_default_supplier(shop) supplier_2 = Supplier.objects.create(name="Supplier 2") 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() # Create coupon that is attached to Supplier 2 coupon = Coupon.objects.create(code="QWERTY", shop=shop, active=True, supplier=supplier_2) # create basket with coupon code campaign = BasketCampaign.objects.create(shop=shop, public_name="test", name="test", active=True, coupon=coupon, supplier=supplier_2) BasketDiscountPercentage.objects.create(campaign=campaign, discount_percentage=Decimal(1)) basket.add_code(coupon.code) # discount is never applied as there is no line # in the basket that matches the coupon's supplier lines_types = [line.type for line in basket.get_final_lines()] assert OrderLineType.DISCOUNT not in lines_types assert basket.product_count == 1 assert basket.total_price.value == Decimal(200) # make supplier be the default supplier coupon.supplier = supplier coupon.save() campaign.supplier = supplier campaign.save() basket.uncache() lines_types = [line.type for line in basket.get_final_lines()] assert OrderLineType.DISCOUNT in lines_types assert basket.product_count == 1 assert basket.total_price.value == Decimal()
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_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_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 test_productdiscountamount(rf): # Buy X amount of Y get Z discount from Y 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" 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_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_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(shop) # 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) basket.shipping_method = get_shipping_method(shop=shop) 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()) == 3 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_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 _create_order(request, customer, coupon, product, expected_product_price): creator = OrderCreator(request) shop = request.shop request.basket = None request.customer = customer basket = get_basket(request) basket.status = factories.get_initial_order_status() basket.add_product(supplier=factories.get_default_supplier(), shop=shop, product=product, quantity=1) basket.shipping_method = factories.get_shipping_method(shop=shop) basket.payment_method = factories.get_payment_method(shop=shop) basket.add_code(coupon) assert basket.shop == request.shop assert basket.customer == request.customer assert product.get_price_info(request).price == expected_product_price creator.create_order(basket)
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_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_fixed_cost_with_waiving_costs(): sm = get_shipping_method(name="Fixed and waiving", price=5) sm.behavior_components.add( *[WaivingCostBehaviorComponent.objects.create( price_value=p, waive_limit_value=w) for (p, w) in [(3, 5), (7, 10), (10, 30)]]) source = BasketishOrderSource(get_default_shop()) source.shipping_method = sm def pricestr(pi): assert pi.price.unit_matches_with(source.create_price(0)) return "%.0f EUR (%.0f EUR)" % (pi.price.value, pi.base_price.value) assert pricestr(sm.get_total_cost(source)) == "25 EUR (25 EUR)" assert source.total_price.value == 25 source.add_line( type=OrderLineType.PRODUCT, product=get_default_product(), base_unit_price=source.create_price(2), quantity=1) assert pricestr(sm.get_total_cost(source)) == "25 EUR (25 EUR)" assert source.total_price.value == 27 source.add_line( type=OrderLineType.PRODUCT, product=get_default_product(), base_unit_price=source.create_price(3), quantity=1) assert pricestr(sm.get_total_cost(source)) == "22 EUR (25 EUR)" assert source.total_price.value == 27 source.add_line( type=OrderLineType.PRODUCT, product=get_default_product(), base_unit_price=source.create_price(10), quantity=1) assert pricestr(sm.get_total_cost(source)) == "15 EUR (25 EUR)" assert source.total_price.value == 30 source.add_line( type=OrderLineType.PRODUCT, product=get_default_product(), base_unit_price=source.create_price(10), quantity=1) assert pricestr(sm.get_total_cost(source)) == "15 EUR (25 EUR)" assert source.total_price.value == 40 source.add_line( type=OrderLineType.PRODUCT, product=get_default_product(), base_unit_price=source.create_price(10), quantity=1) assert pricestr(sm.get_total_cost(source)) == "5 EUR (25 EUR)" assert source.total_price.value == 40
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_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 _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() 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 _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 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_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() 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_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 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_limited_methods(): """ Test that products can declare that they limit available shipping methods. """ unique_shipping_method = get_shipping_method(name="unique", price=0) shop = get_default_shop() common_product = create_product( sku="SH_COMMON", shop=shop) # A product that does not limit shipping methods unique_product = create_product( sku="SH_UNIQUE", shop=shop) # A product that only supports unique_shipping_method unique_shop_product = unique_product.get_shop_instance(shop) unique_shop_product.limit_shipping_methods = True unique_shop_product.shipping_methods.add(unique_shipping_method) unique_shop_product.save() impossible_product = create_product( sku="SH_IMP", shop=shop) # A product that can't be shipped at all imp_shop_product = impossible_product.get_shop_instance(shop) imp_shop_product.limit_shipping_methods = True imp_shop_product.save() for product_ids, method_ids in [ ((common_product.pk, unique_product.pk), (unique_shipping_method.pk, )), ((common_product.pk, ), ShippingMethod.objects.values_list("pk", flat=True)), ((unique_product.pk, ), (unique_shipping_method.pk, )), (( unique_product.pk, impossible_product.pk, ), ()), (( common_product.pk, impossible_product.pk, ), ()), ]: product_ids = set(product_ids) assert ShippingMethod.objects.available_ids( shop=shop, products=product_ids) == set(method_ids)
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 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 _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_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()