def test_get_visible_products_filter(): context = get_jinja_context() shop = get_default_shop() supplier = get_default_supplier() product_1 = create_product( "test-sku-1", supplier=supplier, shop=shop, ) product_2 = create_product( "test-sku-2", supplier=supplier, shop=shop, ) filter_dict = {"id": product_1.id} product_list = general.get_visible_products(context, n_products=2, filter_dict=filter_dict) assert product_1 in product_list assert product_2 not in product_list # Test also with orderable_only False product_list = general.get_visible_products(context, n_products=2, filter_dict=filter_dict, orderable_only=False) assert product_1 in product_list assert product_2 not in product_list
def test_basket_shipping_error(rf): StoredBasket.objects.all().delete() shop = get_default_shop() supplier = get_default_supplier() shipped_product = create_product( printable_gibberish(), shop=shop, supplier=supplier, default_price=50, shipping_mode=ShippingMode.SHIPPED ) unshipped_product = create_product( printable_gibberish(), shop=shop, supplier=supplier, default_price=50, shipping_mode=ShippingMode.NOT_SHIPPED ) request = rf.get("/") request.session = {} request.shop = shop apply_request_middleware(request) basket = get_basket(request) # With a shipped product but no shipping methods, we oughta get an error basket.add_product(supplier=supplier, shop=shop, product=shipped_product, quantity=1) assert any(ve.code == "no_common_shipping" for ve in basket.get_validation_errors()) basket.clear_all() # But with an unshipped product, we should not basket.add_product(supplier=supplier, shop=shop, product=unshipped_product, quantity=1) assert not any(ve.code == "no_common_shipping" for ve in basket.get_validation_errors())
def test_simple_variation(): shop = get_default_shop() parent = create_product("SimpleVarParent") children = [create_product("SimpleVarChild-%d" % x) for x in range(10)] for child in children: child.link_to_parent(parent) sp = ShopProduct.objects.create(shop=shop, product=child, listed=True) assert child.is_variation_child() assert not sp.is_list_visible() # Variation children are not list visible assert parent.mode == ProductMode.SIMPLE_VARIATION_PARENT assert not list(get_all_available_combinations(parent)) # Simple variations can't have these. # Validation tests dummy = create_product("InvalidSimpleVarChild") with pytest.raises(ValueError): dummy.link_to_parent(parent, variables={"size": "XL"}) with pytest.raises(ValueError): parent.link_to_parent(dummy) with pytest.raises(ValueError): dummy.link_to_parent(children[0]) # Unlinkage for child in children: child.unlink_from_parent() assert not child.is_variation_child() assert child.mode == ProductMode.NORMAL assert not parent.is_variation_parent() assert parent.variation_children.count() == 0
def test_package(): shop = get_default_shop() supplier = get_default_supplier() package_product = create_product("PackageParent", shop=shop, supplier=supplier) assert not package_product.get_package_child_to_quantity_map() children = [create_product("PackageChild-%d" % x, shop=shop, supplier=supplier) for x in range(4)] package_def = {child: 1 + i for (i, child) in enumerate(children)} package_product.make_package(package_def) assert package_product.mode == ProductMode.PACKAGE_PARENT package_product.save() sp = package_product.get_shop_instance(shop) assert not list(sp.get_orderability_errors(supplier=supplier, quantity=1, customer=AnonymousContact())) with pytest.raises(ValueError): # Test re-packaging fails package_product.make_package(package_def) # Check that OrderCreator can deal with packages source = BasketishOrderSource() source.lines.append(SourceLine( type=OrderLineType.PRODUCT, product=package_product, supplier=get_default_supplier(), quantity=10, unit_price=TaxlessPrice(10), )) source.shop = get_default_shop() source.status = get_initial_order_status() creator = OrderCreator(request=None) order = creator.create_order(source) pids_to_quantities = order.get_product_ids_and_quantities() for child, quantity in six.iteritems(package_def): assert pids_to_quantities[child.pk] == 10 * quantity
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", discount_amount_value=discount_amount_value, active=True) campaign.conditions.add(rule) campaign.save() 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_price_infos(rf): request, shop, group = initialize_test(rf, True) price = shop.create_price product_one = create_product("Product_1", shop, default_price=150) product_two = create_product("Product_2", shop, default_price=250) spp = SimpleProductPrice(product=product_one, shop=shop, group=group, price_value=100) spp.save() spp = SimpleProductPrice(product=product_two, shop=shop, group=group, price_value=200) spp.save() product_ids = [product_one.pk, product_two.pk] spm = get_pricing_module() assert isinstance(spm, SimplePricingModule) pricing_context = spm.get_context_from_request(request) price_infos = spm.get_price_infos(pricing_context, product_ids) assert len(price_infos) == 2 assert product_one.pk in price_infos assert product_two.pk in price_infos assert price_infos[product_one.pk].price == price(100) assert price_infos[product_two.pk].price == price(200) assert price_infos[product_one.pk].base_price == price(100) assert price_infos[product_two.pk].base_price == price(200)
def test_complex_variation(): request = get_request_with_basket() basket = request.basket shop = get_default_shop() supplier = get_default_supplier() parent = create_product("SuperComplexVarParent", shop=shop, supplier=supplier) color_var = ProductVariationVariable.objects.create(product=parent, identifier="color") size_var = ProductVariationVariable.objects.create(product=parent, identifier="size") ProductVariationVariableValue.objects.create(variable=color_var, identifier="yellow") ProductVariationVariableValue.objects.create(variable=size_var, identifier="small") combinations = list(parent.get_all_available_combinations()) for combo in combinations: child = create_product("xyz-%s" % combo["sku_part"], shop=shop, supplier=supplier) child.link_to_parent(parent, combo["variable_to_value"]) # Elided product should not yield a result yellow_color_value = ProductVariationVariableValue.objects.get(variable=color_var, identifier="yellow") small_size_value = ProductVariationVariableValue.objects.get(variable=size_var, identifier="small") # add to basket yellow + small kwargs = {"var_%d" % color_var.pk: yellow_color_value.pk, "var_%d" % size_var.pk: small_size_value.pk} basket_commands.handle_add_var(request, basket, 1, **kwargs) assert basket.get_product_ids_and_quantities()[child.pk] == 1 with pytest.raises(ValidationError): kwargs = {"var_%d" % color_var.pk: yellow_color_value.pk, "var_%d" % size_var.pk: small_size_value.pk + 1} basket_commands.handle_add_var(request, basket, 1, **kwargs)
def test_multivariable_variation(): parent = create_product("SuperComplexVarParent") color_var = ProductVariationVariable.objects.create(product=parent, identifier="color") size_var = ProductVariationVariable.objects.create(product=parent, identifier="size") for color in ("yellow", "blue", "brown"): ProductVariationVariableValue.objects.create(variable=color_var, identifier=color) for size in ("small", "medium", "large", "huge"): ProductVariationVariableValue.objects.create(variable=size_var, identifier=size) combinations = list(get_all_available_combinations(parent)) assert len(combinations) == (3 * 4) for combo in combinations: assert not combo["result_product_pk"] # Elide a combination (yellow/small) for testing: if combo["variable_to_value"][color_var].identifier == "yellow" and combo["variable_to_value"][size_var].identifier == "small": continue child = create_product("xyz-%s" % combo["sku_part"]) child.link_to_parent(parent, combo["variable_to_value"]) assert parent.mode == ProductMode.VARIABLE_VARIATION_PARENT # Elided product should not yield a result yellow_color_value = ProductVariationVariableValue.objects.get(variable=color_var, identifier="yellow") small_size_value = ProductVariationVariableValue.objects.get(variable=size_var, identifier="small") assert not ProductVariationResult.resolve(parent, {color_var: yellow_color_value, size_var: small_size_value}) # Anything else should brown_color_value = ProductVariationVariableValue.objects.get(variable=color_var, identifier="brown") result1 = ProductVariationResult.resolve(parent, {color_var: brown_color_value, size_var: small_size_value}) result2 = ProductVariationResult.resolve(parent, {color_var.pk: brown_color_value.pk, size_var.pk: small_size_value.pk}) assert result1 and result2 assert result1.pk == result2.pk assert len(get_available_variation_results(parent)) == (3 * 4 - 1)
def test_form_populate_initial_data(rf, admin_user): shop = get_default_shop() supplier = get_default_supplier() initial_discount_amount = 20 campaign = BasketCampaign.objects.create(shop=shop) BasketDiscountAmount.objects.create(campaign=campaign, discount_amount=initial_discount_amount) # Test that correct initial value is returned for non-many-to-many field product_amount_initial = 10 product_amount_condition = BasketTotalProductAmountCondition(product_count=product_amount_initial) product_amount_condition.save() campaign.conditions.add(product_amount_condition) products_count_initial = 5 for i in range(products_count_initial): create_product(printable_gibberish(), shop=shop, supplier=supplier, default_price="20") products_initial = Product.objects.all()[:products_count_initial] assert len(products_initial) == products_count_initial # Test that correct initial value is returned for many-to-many field products_in_basket_condition = ProductsInBasketCondition.objects.create() products_in_basket_condition.values = products_initial products_in_basket_condition.save() campaign.conditions.add(products_in_basket_condition) assert len(campaign.conditions.all()) == 2 assert campaign.effects.count() == 1 request=apply_request_middleware(rf.get("/"), user=admin_user) form = BasketCampaignForm(request=request, instance=campaign) assert form.fields["basket_product_condition"].initial == product_amount_initial assert set(form.fields["basket_products_condition"].initial) == set([p.pk for p in products_initial]) assert form.fields["discount_amount_effect"].initial == initial_discount_amount
def test_simple_variation(): shop = get_default_shop() parent = create_product("SimpleVarParent") children = [create_product("SimpleVarChild-%d" % x) for x in range(10)] for child in children: child.link_to_parent(parent) sp = ShopProduct.objects.create(shop=shop, product=child, listed=True) assert child.is_variation_child() assert not sp.is_list_visible() # Variation children are not list visible assert parent.mode == ProductMode.SIMPLE_VARIATION_PARENT assert not list(parent.get_all_available_combinations()) # Simple variations can't have these. # Validation tests dummy = create_product("InvalidSimpleVarChild") with pytest.raises(ValueError): dummy.link_to_parent(parent, variables={"size": "XL"}) with pytest.raises(ValueError): parent.link_to_parent(dummy) with pytest.raises(ValueError): dummy.link_to_parent(children[0]) # Unlinkage for child in children: child.unlink_from_parent() assert not child.is_variation_child() assert child.mode == ProductMode.NORMAL assert not parent.is_variation_parent() assert parent.variation_children.count() == 0
def test_variable_variation(): parent = create_product("ComplexVarParent") sizes_and_children = [("%sL" % ("X" * x), create_product("ComplexVarChild-%d" % x)) for x in range(4)] for size, child in sizes_and_children: child.link_to_parent(parent, variables={"size": size}) assert parent.mode == ProductMode.VARIABLE_VARIATION_PARENT assert all(child.is_variation_child() for (size, child) in sizes_and_children) # Validation tests dummy = create_product("InvalidComplexVarChild") with pytest.raises(ValueError): dummy.link_to_parent(parent) with pytest.raises(ValueError): parent.link_to_parent(dummy) with pytest.raises(ValueError): dummy.link_to_parent(sizes_and_children[0][1]) # Variable tests size_attr = parent.variation_variables.get(identifier="size") for size, child in sizes_and_children: size_val = size_attr.values.get(identifier=size) result_product = ProductVariationResult.resolve( parent, {size_attr: size_val}) assert result_product == child
def get_frontend_order_state(contact, valid_lines=True): """ Get a dict structure mirroring what the frontend JavaScript would submit. :type contact: Contact|None """ translation.activate("en") shop = get_default_shop() 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() if valid_lines: lines = [ {"id": "x", "type": "product", "product": {"id": product.id}, "quantity": "32", "baseUnitPrice": 50}, {"id": "y", "type": "other", "sku": "hello", "text": "A greeting", "quantity": 1, "unitPrice": "5.5"}, {"id": "z", "type": "text", "text": "This was an order!", "quantity": 0}, ] else: unshopped_product = create_product(sku=printable_gibberish(), supplier=get_default_supplier()) not_visible_product = create_product( sku=printable_gibberish(), supplier=get_default_supplier(), shop=shop ) not_visible_shop_product = not_visible_product.get_shop_instance(shop) not_visible_shop_product.visible = False not_visible_shop_product.save() lines = [ {"id": "x", "type": "product"}, # no product? {"id": "x", "type": "product", "product": {"id": unshopped_product.id}}, # not in this shop? {"id": "y", "type": "product", "product": {"id": -product.id}}, # invalid product? {"id": "z", "type": "other", "quantity": 1, "unitPrice": "q"}, # what's that price? {"id": "rr", "type": "product", "quantity": 1, "product": {"id": not_visible_product.id}} # not visible ] state = { "customer": {"id": contact.id if contact else None}, "lines": lines, "methods": { "shippingMethod": {"id": get_default_shipping_method().id}, "paymentMethod": {"id": get_default_payment_method().id}, }, "shop": { "selected": { "id": shop.id, "name": shop.name, "currency": shop.currency, "priceIncludeTaxes": shop.prices_include_tax } } } return state
def init_test(request, shop, prices): apply_request_middleware(request) parent = create_product("parent_product", shop=shop) children = [create_product("child-%d" % price, shop=shop, default_price=price) for price in prices] for child in children: child.link_to_parent(parent) return parent
def test_simple_children_formset(): FormSet = formset_factory(SimpleVariationChildForm, SimpleVariationChildFormSet, extra=5, can_delete=True) parent = create_product(printable_gibberish()) child = create_product(printable_gibberish()) # No links yet formset = FormSet(parent_product=parent) assert formset.initial_form_count() == 0 # No children yet # Save a link data = dict(get_form_data(formset, True), **{"form-0-child": child.pk}) formset = FormSet(parent_product=parent, data=data) formset.save() assert parent.variation_children.filter( pk=child.pk).exists() # Got link'd! # Remove the link formset = FormSet(parent_product=parent) assert formset.initial_form_count() == 1 # Got the child here data = dict(get_form_data(formset, True), **{"form-0-DELETE": "1"}) formset = FormSet(parent_product=parent, data=data) formset.save() assert not parent.variation_children.exists() # Got unlinked
def test_price_infos(rf): request, shop, group = initialize_test(rf, True) product_one = create_product("Product_1", shop, default_price=150) product_two = create_product("Product_2", shop, default_price=250) spp = SimpleProductPrice(product=product_one, shop=shop, group=group, price=100) spp.save() spp = SimpleProductPrice(product=product_two, shop=shop, group=group, price=200) spp.save() product_ids = [product_one.pk, product_two.pk] spm = SimplePricingModule() pricing_context = spm.get_context_from_request(request) price_infos = spm.get_price_infos(pricing_context, product_ids) assert len(price_infos) == 2 assert product_one.pk in price_infos assert product_two.pk in price_infos assert price_infos[product_one.pk].price == TaxfulPrice(100) assert price_infos[product_two.pk].price == TaxfulPrice(200) assert price_infos[product_one.pk].base_price == TaxfulPrice(100) assert price_infos[product_two.pk].base_price == TaxfulPrice(200)
def test_form_populate_initial_data(rf, admin_user): shop = get_default_shop() supplier = get_default_supplier() campaign = BasketCampaign(discount_percentage=0.1, shop=shop) campaign.save() # Test that correct initial value is returned for non-many-to-many field product_amount_initial = 10 product_amount_condition = BasketTotalProductAmountCondition(product_count=product_amount_initial) product_amount_condition.save() campaign.conditions.add(product_amount_condition) products_count_initial = 5 for i in range(products_count_initial): create_product(printable_gibberish(), shop=shop, supplier=supplier, default_price="20") products_initial = Product.objects.all()[:products_count_initial] assert len(products_initial) == products_count_initial # Test that correct initial value is returned for many-to-many field products_in_basket_condition = ProductsInBasketCondition.objects.create() products_in_basket_condition.values = products_initial products_in_basket_condition.save() campaign.conditions.add(products_in_basket_condition) assert len(campaign.conditions.all()) == 2 request=apply_request_middleware(rf.get("/"), user=admin_user) form = BasketCampaignForm(request=request, instance=campaign) assert form.fields["basket_product_condition"].initial == product_amount_initial assert set(form.fields["basket_products_condition"].initial) == set([p.pk for p in products_initial])
def test_multivariable_variation(): parent = create_product("SuperComplexVarParent") color_var = ProductVariationVariable.objects.create(product=parent, identifier="color") size_var = ProductVariationVariable.objects.create(product=parent, identifier="size") for color in ("yellow", "blue", "brown"): ProductVariationVariableValue.objects.create(variable=color_var, identifier=color) for size in ("small", "medium", "large", "huge"): ProductVariationVariableValue.objects.create(variable=size_var, identifier=size) combinations = list(parent.get_all_available_combinations()) assert len(combinations) == (3 * 4) for combo in combinations: assert not combo["result_product_pk"] # Elide a combination (yellow/small) for testing: if combo["variable_to_value"][color_var].identifier == "yellow" and combo["variable_to_value"][size_var].identifier == "small": continue child = create_product("xyz-%s" % combo["sku_part"]) child.link_to_parent(parent, combo["variable_to_value"]) assert parent.mode == ProductMode.VARIABLE_VARIATION_PARENT # Elided product should not yield a result yellow_color_value = ProductVariationVariableValue.objects.get(variable=color_var, identifier="yellow") small_size_value = ProductVariationVariableValue.objects.get(variable=size_var, identifier="small") assert not ProductVariationResult.resolve(parent, {color_var: yellow_color_value, size_var: small_size_value}) # Anything else should brown_color_value = ProductVariationVariableValue.objects.get(variable=color_var, identifier="brown") result1 = ProductVariationResult.resolve(parent, {color_var: brown_color_value, size_var: small_size_value}) result2 = ProductVariationResult.resolve(parent, {color_var.pk: brown_color_value.pk, size_var.pk: small_size_value.pk}) assert result1 and result2 assert result1.pk == result2.pk assert len(parent.get_available_variation_results()) == (3 * 4 - 1)
def test_price_infos(rf): request, shop, group = initialize_test(rf, True) price = shop.create_price product_one = create_product("Product_1", shop, default_price=150) product_two = create_product("Product_2", shop, default_price=250) spp = CgpPrice(product=product_one, shop=shop, group=group, price_value=100) spp.save() spp = CgpPrice(product=product_two, shop=shop, group=group, price_value=200) spp.save() product_ids = [product_one.pk, product_two.pk] spm = get_pricing_module() assert isinstance(spm, CustomerGroupPricingModule) pricing_context = spm.get_context_from_request(request) price_infos = spm.get_price_infos(pricing_context, product_ids) assert len(price_infos) == 2 assert product_one.pk in price_infos assert product_two.pk in price_infos assert price_infos[product_one.pk].price == price(100) assert price_infos[product_two.pk].price == price(200) assert price_infos[product_one.pk].base_price == price(100) assert price_infos[product_two.pk].base_price == price(200)
def test_variable_variation(): parent = create_product("ComplexVarParent") sizes_and_children = [("%sL" % ("X" * x), create_product("ComplexVarChild-%d" % x)) for x in range(4)] for size, child in sizes_and_children: child.link_to_parent(parent, variables={"size": size}) assert parent.mode == ProductMode.VARIABLE_VARIATION_PARENT assert all(child.is_variation_child() for (size, child) in sizes_and_children) # Validation tests dummy = create_product("InvalidComplexVarChild") with pytest.raises(ValueError): dummy.link_to_parent(parent) with pytest.raises(ValueError): parent.link_to_parent(dummy) with pytest.raises(ValueError): dummy.link_to_parent(sizes_and_children[0][1]) # Variable tests size_attr = parent.variation_variables.get(identifier="size") for size, child in sizes_and_children: size_val = size_attr.values.get(identifier=size) result_product = ProductVariationResult.resolve(parent, {size_attr: size_val}) assert result_product == child
def test_price_infos_are_discounted(rf): request = initialize_test(rf, True) price = request.shop.create_price product_one = create_product("Product_1", request.shop, default_price=150) product_two = create_product("Product_2", request.shop, default_price=250) spp = DiscountedProductPrice(product=product_one, shop=request.shop, price_value=100) spp.save() spp = DiscountedProductPrice(product=product_two, shop=request.shop, price_value=200) spp.save() product_ids = [product_one.pk, product_two.pk] dpm = DiscountPricingModule() pricing_context = dpm.get_context_from_request(request) price_infos = dpm.get_price_infos(pricing_context, product_ids) assert len(price_infos) == 2 assert product_one.pk in price_infos assert product_two.pk in price_infos first_price_info = price_infos[product_one.pk] second_price_info = price_infos[product_two.pk] assert first_price_info.price == price(100) assert first_price_info.base_price == price(150) assert first_price_info.is_discounted assert second_price_info.price == price(200) assert second_price_info.base_price == price(250) assert second_price_info.is_discounted
def test_protected_fields(): activate("en") shop = Shop.objects.create( name="testshop", identifier="testshop", status=ShopStatus.ENABLED, public_name="test shop", domain="derp", currency="EUR" ) assert shop.name == "testshop" assert shop.currency == "EUR" shop_form = ShopBaseForm(instance=shop, languages=settings.LANGUAGES) assert not shop_form._get_protected_fields() # No protected fields just yet, right? data = get_form_data(shop_form, prepared=True) shop_form = ShopBaseForm(data=data, instance=shop, languages=settings.LANGUAGES) _test_cleanliness(shop_form) shop_form.save() # Now let's make it protected! create_product(printable_gibberish(), shop=shop, supplier=get_default_supplier()) order = create_random_order(customer=create_random_person(), shop=shop) assert order.shop == shop # And try again... data["currency"] = "XBT" # Bitcoins! shop_form = ShopBaseForm(data=data, instance=shop, languages=settings.LANGUAGES) assert shop_form._get_protected_fields() # So protected! _test_cleanliness(shop_form) shop = shop_form.save() assert shop.currency == "EUR" # But the shop form ignored the change . . .
def test_best_selling_products_with_multiple_orders(): context = get_jinja_context() supplier = get_default_supplier() shop = get_default_shop() n_products = 2 price = 10 product_1 = create_product("test-sku-1", supplier=supplier, shop=shop) product_2 = create_product("test-sku-2", supplier=supplier, shop=shop) create_order_with_product(product_1, supplier, quantity=1, taxless_base_unit_price=price, shop=shop) create_order_with_product(product_2, supplier, quantity=1, taxless_base_unit_price=price, shop=shop) cache.clear() # Two initial products sold assert product_1 in general.get_best_selling_products(context, n_products=n_products) assert product_2 in general.get_best_selling_products(context, n_products=n_products) product_3 = create_product("test-sku-3", supplier=supplier, shop=shop) create_order_with_product(product_3, supplier, quantity=2, taxless_base_unit_price=price, shop=shop) cache.clear() # Third product sold in greater quantity assert product_3 in general.get_best_selling_products(context, n_products=n_products) create_order_with_product(product_1, supplier, quantity=4, taxless_base_unit_price=price, shop=shop) create_order_with_product(product_2, supplier, quantity=4, taxless_base_unit_price=price, shop=shop) cache.clear() # Third product outsold by first two products assert product_3 not in general.get_best_selling_products(context, n_products=n_products)
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_admin_form(rf, admin_user): supplier = get_simple_supplier() shop = get_default_shop() product = create_product("simple-test-product", shop, supplier) request = rf.get("/") request.user = admin_user frm = SimpleSupplierForm(product=product, request=request) # Form contains 1 product even if the product is not stocked assert len(frm.products) == 1 assert not frm.products[0].is_stocked() product.stock_behavior = StockBehavior.STOCKED # Make product stocked product.save() # Now since product is stocked it should be in the form frm = SimpleSupplierForm(product=product, request=request) assert len(frm.products) == 1 # Add stocked children for product child_product = create_product("child-test-product", shop, supplier) child_product.stock_behavior = StockBehavior.STOCKED child_product.save() child_product.link_to_parent(product) # Admin form should now contain only child products for product frm = SimpleSupplierForm(product=product, request=request) assert len(frm.products) == 1 assert frm.products[0] == child_product
def test_price_infos_are_discounted(rf): request = initialize_test(rf, True) price = request.shop.create_price product_one = create_product("Product_1", request.shop, default_price=150) product_two = create_product("Product_2", request.shop, default_price=250) spp = DiscountedProductPrice(product=product_one, shop=request.shop, price_value=100) spp.save() spp = DiscountedProductPrice(product=product_two, shop=request.shop, price_value=200) spp.save() product_ids = [product_one.pk, product_two.pk] dpm = get_pricing_module() pricing_context = dpm.get_context_from_request(request) price_infos = dpm.get_price_infos(pricing_context, product_ids) assert len(price_infos) == 2 assert product_one.pk in price_infos assert product_two.pk in price_infos first_price_info = price_infos[product_one.pk] second_price_info = price_infos[product_two.pk] assert first_price_info.price == price(100) assert first_price_info.base_price == price(150) assert first_price_info.is_discounted assert second_price_info.price == price(200) assert second_price_info.base_price == price(250) assert second_price_info.is_discounted
def initialize_test(rf): shop = get_default_shop() request = rf.get("/") request.shop = shop apply_request_middleware(request) product1 = create_product("test-product1", shop=shop, default_price=120) product2 = create_product("test-product2", shop=shop, default_price=180) return (request, [product1, product2], shop.create_price)
def test_basket_free_product_coupon(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 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.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) == 2 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 == OrderLineType.PRODUCT if line.product != product: assert line.product == second_product
def test_variable_variation_form(): var1 = printable_gibberish() var2 = printable_gibberish() parent = create_product(printable_gibberish()) for a in range(4): for b in range(3): child = create_product(printable_gibberish()) child.link_to_parent(parent, variables={var1: a, var2: b}) assert parent.variation_children.count() == 4 * 3 form = VariableVariationChildrenForm(parent_product=parent) assert len(form.fields) == 12
def test_package_product_page(client): shop = get_default_shop() parent = create_product("test-sku-1", shop=shop) child = create_product("test-sku-2", shop=shop) parent.make_package({child: 2}) assert parent.is_package_parent() response = client.get( reverse('shoop:product', kwargs={ 'pk': parent.pk, 'slug': parent.slug })) assert b'no such element' not in response.content, 'All items are not rendered correctly'
def test_best_selling_products_with_multiple_orders(): context = get_jinja_context() supplier = get_default_supplier() shop = get_default_shop() n_products = 2 price = 10 product_1 = create_product("test-sku-1", supplier=supplier, shop=shop) product_2 = create_product("test-sku-2", supplier=supplier, shop=shop) create_order_with_product(product_1, supplier, quantity=1, taxless_base_unit_price=price, shop=shop) create_order_with_product(product_2, supplier, quantity=1, taxless_base_unit_price=price, shop=shop) cache.clear() # Two initial products sold assert product_1 in general.get_best_selling_products( context, n_products=n_products) assert product_2 in general.get_best_selling_products( context, n_products=n_products) product_3 = create_product("test-sku-3", supplier=supplier, shop=shop) create_order_with_product(product_3, supplier, quantity=2, taxless_base_unit_price=price, shop=shop) cache.clear() # Third product sold in greater quantity assert product_3 in general.get_best_selling_products( context, n_products=n_products) create_order_with_product(product_1, supplier, quantity=4, taxless_base_unit_price=price, shop=shop) create_order_with_product(product_2, supplier, quantity=4, taxless_base_unit_price=price, shop=shop) cache.clear() # Third product outsold by first two products assert product_3 not in general.get_best_selling_products( context, n_products=n_products)
def get_package_product(): """ :rtype: shoop.core.models.Product """ shop = get_default_shop() supplier = get_default_supplier() package_product = create_product("PackageParent", shop=shop, supplier=supplier) assert not package_product.get_package_child_to_quantity_map() children = [create_product("PackageChild-%d" % x, shop=shop, supplier=supplier) for x in range(4)] package_def = {child: 1 + i for (i, child) in enumerate(children)} package_product.make_package(package_def) assert package_product.is_package_parent() package_product.save() return package_product
def test_variation(): request = get_request_with_basket() basket = request.basket shop = get_default_shop() supplier = get_default_supplier() parent = create_product("BuVarParent", shop=shop, supplier=supplier) child = create_product("BuVarChild", shop=shop, supplier=supplier) child.link_to_parent(parent, variables={"test": "very"}) attr = parent.variation_variables.get(identifier="test") val = attr.values.get(identifier="very") basket_commands.handle_add_var(request, basket, 1, **{"var_%s" % attr.id: val.id}) assert basket.get_product_ids_and_quantities()[child.pk] == 1 with pytest.raises(ValidationError): basket_commands.handle_add_var(request, basket, 1, **{"var_%s" % attr.id: (val.id + 1)})
def test_cross_sell_plugin_renders(): """ Test that the plugin renders a product """ shop = get_default_shop() product = create_product("test-sku", shop=shop, stock_behavior=StockBehavior.UNSTOCKED) computed = create_product("test-computed-sku", shop=shop, stock_behavior=StockBehavior.UNSTOCKED) type = ProductCrossSellType.COMPUTED ProductCrossSell.objects.create(product1=product, product2=computed, type=type) assert ProductCrossSell.objects.filter(product1=product, type=type).count() == 1 context = get_jinja_context(product=product) rendered = ProductCrossSellsPlugin({"type": type}).render(context) assert computed.sku in rendered
def test_package_product_page(client): shop = get_default_shop() parent = create_product("test-sku-1", shop=shop) child = create_product("test-sku-2", shop=shop) parent.make_package({child: 2}) assert parent.is_package_parent() response = client.get( reverse('shoop:product', kwargs={ 'pk': parent.pk, 'slug': parent.slug } ) ) assert b'no such element' not in response.content, 'All items are not rendered correctly'
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) dc = Coupon.objects.create(code="TEST", active=True) campaign = BasketCampaign.objects.create( shop=shop, name="test", public_name="test", coupon=dc, discount_amount_value=shop.create_price("20"), active=True ) 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() == 2 assert OrderLineType.DISCOUNT in [l.type for l in order.lines.all()] return order
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}, "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_start_end_dates(rf): activate("en") original_price = "180" discounted_price = "160" request, shop, group = initialize_test(rf, False) cat = Category.objects.create(name="test") rule1, rule2 = create_condition_and_filter(cat, request) campaign = CatalogCampaign.objects.create(shop=shop, name="test", discount_amount_value="20", active=True) campaign.conditions.add(rule1) campaign.save() price = shop.create_price product = create_product("Just-A-Product-Too", shop, default_price=original_price) today = now() # starts in future campaign.start_datetime = (today + datetime.timedelta(days=2)) campaign.save() assert not campaign.is_available() assert product.get_price_info(request, quantity=1).price == price(original_price) # has already started campaign.start_datetime = (today - datetime.timedelta(days=2)) campaign.save() assert product.get_price_info(request, quantity=1).price == price(discounted_price) # already ended campaign.end_datetime = (today - datetime.timedelta(days=1)) campaign.save() assert not campaign.is_available() assert product.get_price_info(request, quantity=1).price == price(original_price) # not ended yet campaign.end_datetime = (today + datetime.timedelta(days=1)) campaign.save() assert product.get_price_info(request, quantity=1).price == price(discounted_price) # no start datetime campaign.start_datetime = None campaign.save() assert product.get_price_info(request, quantity=1).price == price(discounted_price) # no start datetime but ended campaign.end_datetime = (today - datetime.timedelta(days=1)) campaign.save() assert not campaign.is_available() assert product.get_price_info(request, quantity=1).price == price(original_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) original_line_count = len(basket.get_final_lines()) assert original_line_count == 1 assert basket.product_count == 1 original_price = basket.total_price campaign = BasketCampaign.objects.create(shop=request.shop, name="test", public_name="test", active=True) BasketDiscountAmount.objects.create( campaign=campaign, discount_amount=campaign_discount_value) for condition in conditions: campaign.conditions.add(condition) assert campaign.is_available() return basket, original_line_count, original_price
def test_module(address, expected_taxes): """ Test the DefaultTaxModule. """ # Create a product shop = get_shop(prices_include_tax=False, currency='USD') product = create_product('PROD', shop=shop, default_price=1000) price = product.get_shop_instance(shop).default_price # Put the tax rules into database for ruledef in shuffled(TAX_RULE_DEFS): rule = ruledef.get_tax_rule() rule.tax.save() rule.tax = rule.tax # refresh the id rule.save() rule.tax_classes.add(product.tax_class) assert TaxRule.objects.count() == len(TAX_RULE_DEFS) with override_settings(SHOOP_TAX_MODULE='default_tax'): module = get_tax_module() assert isinstance(module, DefaultTaxModule) context = TaxingContext(location=address) taxed_price = module.get_taxed_price_for(context, product, price) expected_codes = set(sum([x.split() for x in expected_taxes], [])) assert set(x.tax.code for x in taxed_price.taxes) == expected_codes expected_tax = Money(TAX_AMOUNTS[expected_taxes], 'USD') assert taxed_price.taxful.amount == price.amount + expected_tax # Clean-up the rules TaxRule.objects.all().delete()
def test_ajax_select_view_with_products(rf): activate("en") view = MultiselectAjaxView.as_view() results = _get_search_results(rf, view, "shoop.Product", "some str") assert len(results) == 0 product_name_en = "The Product" product = create_product("the product", **{"name": product_name_en}) product_name_fi = "product" product.set_current_language("fi") # Making sure we are not getting duplicates from translations product.name = "product" # It seems that finnish translation overlaps with english name product.save() view = MultiselectAjaxView.as_view() results = _get_search_results(rf, view, "shoop.Product", "some str") assert len(results) == 0 results = _get_search_results(rf, view, "shoop.Product", "product") assert len(results) == 1 assert results[0].get("id") == product.id assert results[0].get("name") == product_name_en activate("fi") results = _get_search_results(rf, view, "shoop.Product", "product") assert len(results) == 1 assert results[0].get("id") == product.id assert results[0].get("name") == product_name_fi
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 atleast 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", discount_percentage=discount_percentage, active=True ) campaign.conditions.add(rule) campaign.save() assert len(basket.get_final_lines()) == 2 assert basket.product_count == 1 assert basket.total_price == expected_discounted_price
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) campaign = BasketCampaign.objects.create( shop=shop, public_name="test", name="test", discount_amount_value=discount1, active=True ) campaign.conditions.add(rule) campaign.save() campaign = BasketCampaign.objects.create( shop=shop, public_name="test", name="test", discount_amount_value=discount2, active=True ) campaign.conditions.add(rule) campaign.save() 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_price_cannot_be_under_zero(rf): activate("en") discount_amount = "200" original_price = "199.20" request, shop, group = initialize_test(rf, False) cat = Category.objects.create(name="test") rule1, rule2 = create_condition_and_filter(cat, request) campaign = CatalogCampaign.objects.create( shop=shop, name="test", discount_amount_value=discount_amount, active=True) campaign.conditions.add(rule1) campaign.filters.add(rule2) campaign.save() price = shop.create_price product = create_product("Just-A-Product-Too", shop, default_price=original_price) shop_product = product.get_shop_instance(shop) shop_product.categories.add(cat) shop_product.save() assert product.get_price_info(request, quantity=1).price == price("0")
def test_coupon_amount_limit(): coupon = Coupon.objects.create(code="TEST", active=True) get_default_campaign(coupon) contact = create_random_person() shop = get_default_shop() product = create_product("test", shop=shop, supplier=get_default_supplier(), default_price="12") order = create_random_order(customer=contact) for x in range(50): coupon.use(order) assert coupon.usages.count() == 50 coupon.increase_usage_limit_by(5) coupon.save() assert coupon.usage_limit == 55 assert coupon.can_use_code(contact) for x in range(5): coupon.use(order) assert coupon.usages.count() == 55 assert not Coupon.is_usable(coupon.code, order.customer) assert coupon.usages.count() == 55 # no change, limit met
def test_extending_shipment_form_valid_hook(rf, admin_user): shop = get_default_shop() supplier = get_default_supplier() product = create_product(sku="test-sku", shop=shop, supplier=supplier, default_price=3.33) quantity = 1 order = create_order_with_product(product, supplier, quantity=quantity, taxless_base_unit_price=1, shop=shop) extend_form_class = "shoop_tests.admin.test_shipment_creator.ShipmentFormModifierTest" with override_provides(FORM_MODIFIER_PROVIDER_KEY, [extend_form_class]): phone_number = "+358911" data = {"q_%s" % product.pk: 1, "supplier": supplier.pk, "phone": phone_number} request = apply_request_middleware(rf.post("/", data=data), user=admin_user) view = OrderCreateShipmentView.as_view() response = view(request, pk=order.pk) assert response.status_code == 302 # Order should now have shipment, but let's re fetch it first order = Order.objects.get(pk=order.pk) assert order.shipments.count() == 1 shipment = order.shipments.first() assert order.shipping_data.get(shipment.identifier).get("phone") == phone_number assert shipment.supplier_id == supplier.id assert shipment.products.count() == 1 assert shipment.products.first().product_id == product.id
def test_cross_sell_plugin_type(): """ Test that template helper returns correct number of cross sells when shop contains multiple relation types """ shop = get_default_shop() supplier = get_default_supplier() product = create_product("test-sku", shop=shop, supplier=supplier, stock_behavior=StockBehavior.UNSTOCKED) context = get_jinja_context(product=product) type_counts = ((ProductCrossSellType.RELATED, 1), (ProductCrossSellType.RECOMMENDED, 2), (ProductCrossSellType.BOUGHT_WITH, 3)) # Create cross sell products and relations in different quantities for type, count in type_counts: _create_cross_sell_products(product, shop, supplier, type, count) assert ProductCrossSell.objects.filter(product1=product, type=type).count() == count # Make sure quantities returned by plugin match for type, count in type_counts: assert len( list( product_helpers.get_product_cross_sells( context, product, type, count))) == count
def test_category_filter(rf): request, shop, group = initialize_test(rf, False) cat = get_default_category() cat_filter = CategoryFilter.objects.create() cat_filter.categories.add(cat) cat_filter.save() assert cat_filter.values.first() == cat category = Category.objects.create( parent=None, identifier="testcat", name="catcat", ) cat_filter.values = [cat, category] cat_filter.save() assert cat_filter.values.count() == 2 product = create_product("Just-A-Product-Too", shop, default_price="200") shop_product = product.get_shop_instance(shop) shop_product.categories.add(cat) shop_product.save() assert cat_filter.filter_queryset(ShopProduct.objects.all()).exists() # filter matches
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) 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() == 2 assert OrderLineType.DISCOUNT in [l.type for l in order.lines.all()] return order
def test_product_caching_object(): shop_product = get_default_shop_product() product = shop_product.product another_product = create_product("PCOTestProduct") pco = ProductCachingObject() pco.product = product assert pco.product is product assert pco.product_id == product.pk assert ProductCachingObject().product != pco.product # Assert PCOs are separate assert pco._product_cache == pco.product # This private property is courtesy of ModelCachingDescriptor pco = ProductCachingObject() pco.product_id = product.pk assert pco.product == product assert pco.product_id == product.pk # Not creating a new PCO here pco.product = another_product assert pco.product == another_product assert pco.product_id == another_product.pk # Nor here pco.product_id = product.pk assert pco.product == product assert pco.product_id == product.pk