def test_view_custom_mass_actions(rf, admin_user): factories.get_default_shop() request = apply_request_middleware(rf.get("/", {"jq": json.dumps({"perPage": 100, "page": 1})}), user=admin_user) list_view_func = ManufacturerListView.as_view() # no mass actions response = list_view_func(request) data = json.loads(response.content.decode("utf-8")) assert not data["massActions"] # test with specific key with override_provides("manufacturer_list_mass_actions_provider", [ "shuup.testing.modules.mocker.mass_actions:DummyMassActionProvider" ]): response = list_view_func(request) data = json.loads(response.content.decode("utf-8")) identifiers = [action["key"] for action in data["massActions"]] assert "dummy_mass_action_1" in identifiers assert "dummy_mass_action_2" in identifiers list_view_func = SalesUnitListView.as_view() # test with global with override_provides("admin_mass_actions_provider", [ "shuup.testing.modules.mocker.mass_actions:DummyMassActionProvider" ]): response = list_view_func(request) data = json.loads(response.content.decode("utf-8")) identifiers = [action["key"] for action in data["massActions"]] assert "dummy_mass_action_1" in identifiers assert "dummy_mass_action_2" in identifiers
def test_contact_details_view_with_many_groups(rf, admin_user): get_default_shop() person = create_random_person() person.groups.add( ContactGroup.objects.create(name="Czz Group"), ContactGroup.objects.create(name="Azz Group"), ContactGroup.objects.create(name="Bzz Group"), ContactGroup.objects.language('fi').create(name="Dzz ryhmä"), ) # Group with name in two languages grp_e = ContactGroup.objects.language('en').create(name="Ezz Group") grp_e.set_current_language('fi') grp_e.name = "Ezz ryhmä" grp_e.save() person.groups.add(grp_e) request = apply_request_middleware(rf.get("/"), user=admin_user) with translation.override('en'): view_func = ContactDetailView.as_view() response = view_func(request, pk=person.pk) content = response.render().content.decode('utf-8') assert "Azz Group" in content assert "Bzz Group" in content assert "Czz Group" in content assert "Dzz ryhmä" in content, "no name in active language, still present" assert "Ezz Group" in content, "rendered with active language" positions = [content.index(x + "zz ") for x in 'ABCDE'] assert positions == sorted(positions), "Groups are sorted" assert response.status_code == 200
def test_refund_entire_order(): shop = get_default_shop() supplier = get_simple_supplier() product = create_product( "test-sku", shop=get_default_shop(), default_price=10, ) supplier.adjust_stock(product.id, 5) check_stock_counts(supplier, product, 5, 5) order = create_order_with_product(product, supplier, 2, 200, Decimal("0.24"), shop=shop) order.cache_prices() original_total_price = order.taxful_total_price check_stock_counts(supplier, product, 5, 3) # Create a full refund with `restock_products` set to False order.create_full_refund(restock_products=False) # Confirm the refund was created with correct amount assert order.taxless_total_price.amount.value == 0 assert order.taxful_total_price.amount.value == 0 refund_line = order.lines.order_by("ordering").last() assert refund_line.type == OrderLineType.REFUND assert refund_line.taxful_price == -original_total_price # Make sure logical count reflects refunded products check_stock_counts(supplier, product, 5, 3)
def test_behavior_delete_save(rf, admin_user, view, model, get_object, service_provider_attr): """ Only testing one initial behavior component """ get_default_shop() with override_settings(LANGUAGES=[("en", "en")]): object = get_object() view = view.as_view() service_provider_attr_field = "base-%s" % service_provider_attr component = WeightLimitsBehaviorComponent.objects.create(min_weight=0, max_weight=1) object.behavior_components.add(component) components_before = object.behavior_components.count() assert components_before == 1 data = get_default_data(object, service_provider_attr, service_provider_attr_field, delete=True) data["weightlimitsbehaviorcomponent-0-id"] = component.id data["weightlimitsbehaviorcomponent-INITIAL_FORMS"] = 1 data["weightlimitsbehaviorcomponent-TOTAL_FORMS"] = 2 request = apply_request_middleware(rf.post("/", data=data, user=admin_user)) response = view(request, pk=object.pk) if hasattr(response, "render"): response.render() components_after = object.behavior_components.count() assert not components_after assert not WaivingCostBehaviorComponent.objects.first()
def test_refund_without_shipment(restock): shop = get_default_shop() supplier = get_simple_supplier() product = create_product( "test-sku", shop=get_default_shop(), default_price=10, ) # Start out with a supplier with quantity of 10 of a product supplier.adjust_stock(product.id, 10) check_stock_counts(supplier, product, physical=10, logical=10) order = create_order_with_product(product, supplier, 2, 200, shop=shop) order.cache_prices() check_stock_counts(supplier, product, physical=10, logical=8) # Restock value shouldn't matter if we don't have any shipments product_line = order.lines.first() order.create_refund([ {"line": product_line, "quantity": 2, "amount": Money(400, order.currency), "restock_products": restock}]) if restock: check_stock_counts(supplier, product, physical=10, logical=10) else: check_stock_counts(supplier, product, physical=10, logical=8) assert product_line.refunded_quantity == 2 assert order.get_total_tax_amount() == Money( order.taxful_total_price_value - order.taxless_total_price_value, order.currency)
def test_rules_and_effects(rf, admin_user): """ To make things little bit more simple let's use only english as a language. """ get_default_shop() with override_settings(LANGUAGES=[("en", "en")]): shop = get_default_shop() object = BasketCampaign.objects.create(name="test campaign", active=True, shop=shop) assert object.conditions.count() == 0 assert object.discount_effects.count() == 0 view = BasketCampaignEditView.as_view() data = { "base-name": "test campaign", "base-public_name__en": "Test Campaign", "base-shop": get_default_shop().id, "base-active": True, "base-basket_line_text": "Test campaign activated!" } with override_provides( "campaign_basket_condition", ["shuup.campaigns.admin_module.forms:BasketTotalProductAmountConditionForm"]): with override_provides( "campaign_basket_discount_effect_form", ["shuup.campaigns.admin_module.forms:BasketDiscountAmountForm"]): with override_provides("campaign_basket_line_effect_form", []): data.update(get_products_in_basket_data()) data.update(get_free_product_data(object)) request = apply_request_middleware(rf.post("/", data=data), user=admin_user) view(request, pk=object.pk) object.refresh_from_db() assert object.conditions.count() == 1 assert object.discount_effects.count() == 1
def test_campaign_new_mode_view_formsets(rf, admin_user): view = BasketCampaignEditView get_default_shop() request = apply_request_middleware(rf.get("/"), user=admin_user) form_parts = get_form_parts(request, view, view.model()) assert len(form_parts) == 1 assert issubclass(form_parts[0].__class__, BasketBaseFormPart)
def test_services_edit_view_formsets(rf, admin_user, view, get_object): get_default_shop() object = get_object() request = apply_request_middleware(rf.get("/"), user=admin_user) form_parts = get_form_parts(request, view, object) # form parts should include forms, form parts and plus one for the base form assert len(form_parts) == (len(DEFAULT_BEHAVIOR_FORMS) + len(DEFAULT_BEHAVIOR_FORM_PARTS) + 1)
def test_campaign_edit_save(rf, admin_user): """ To make things little bit more simple let's use only english as a language. """ with override_settings(LANGUAGES=[("en", "en")]): shop = get_default_shop() object = BasketCampaign.objects.create(name="test campaign", active=True, shop=shop) object.save() view = BasketCampaignEditView.as_view() new_name = "Test Campaign" assert object.name != new_name data = { "base-name": new_name, "base-public_name__en": "Test Campaign", "base-shop": get_default_shop().id, "base-active": True, "base-basket_line_text": "Test campaign activated!" } methods_before = BasketCampaign.objects.count() # Conditions and effects is tested separately with override_provides("campaign_basket_condition", []): with override_provides("campaign_basket_discount_effect_form", []): with override_provides("campaign_basket_line_effect_form", []): request = apply_request_middleware(rf.post("/", data=data), user=admin_user) response = view(request, pk=object.pk) assert response.status_code in [200, 302] assert BasketCampaign.objects.count() == methods_before assert BasketCampaign.objects.get(pk=object.pk).name == new_name
def test_login_with_email_3(client, regular_user, rf): if "shuup.front.apps.auth" not in settings.INSTALLED_APPS: pytest.skip("Need shuup.front.apps.auth in INSTALLED_APPS") new_user_password = "******" new_user = get_user_model().objects.create_user( username=regular_user.email, password=new_user_password, email=regular_user.email ) get_default_shop() prepare_user(regular_user) redirect_target = "/redirect-success/" # Login with new_user username should work even if there is users with same email response = client.post(reverse("shuup:login"), data={ "username": regular_user.email, "password": new_user_password, REDIRECT_FIELD_NAME: redirect_target }) assert response.get("location") assert response.get("location").endswith(redirect_target) request = rf.get("/") request.session = client.session assert get_user(request) == new_user, "User is logged in"
def test_login_inactive_user_fails(client, regular_user, rf): if "shuup.front.apps.auth" not in settings.INSTALLED_APPS: pytest.skip("Need shuup.front.apps.auth in INSTALLED_APPS") get_default_shop() prepare_user(regular_user) response = client.post(reverse("shuup:login"), data={ "username": regular_user.username, "password": REGULAR_USER_PASSWORD, }) request = rf.get("/") request.session = client.session assert get_user(request) == regular_user, "User is logged in" request = rf.get("/") request.session = client.session logout(request) user_contact = regular_user.contact assert user_contact.is_active user_contact.is_active = False user_contact.save() client.post(reverse("shuup:login"), data={ "username": regular_user.username, "password": REGULAR_USER_PASSWORD, }) request = rf.get("/") request.session = client.session assert get_user(request).is_anonymous(), "User is still anonymous"
def test_single_page_checkout_with_login_and_register(browser, live_server, settings): # initialize product_name = "Test Product" get_default_shop() pm = get_default_payment_method() sm = get_default_shipping_method() product = create_orderable_product(product_name, "test-123", price=100) OrderStatus.objects.create( identifier="initial", role=OrderStatusRole.INITIAL, name="initial", default=True ) # Initialize test and go to front page browser = initialize_front_browser_test(browser, live_server) wait_until_condition(browser, lambda x: x.is_text_present("Welcome to Default!")) navigate_to_checkout(browser, product) # Let's assume that after addresses the checkout is normal wait_until_condition(browser, lambda x: x.is_text_present("Checkout Method")) test_username = "******" test_email = "*****@*****.**" test_password = "******" register_test(browser, live_server, test_username, test_email, test_password) login_and_finish_up_the_checkout(browser, live_server, test_username, test_email, test_password)
def test_category_links_plugin_with_customer(rf, show_all_categories): """ Test plugin for categories that is visible for certain group """ shop = get_default_shop() group = get_default_customer_group() customer = create_random_person() customer.groups.add(group) customer.save() request = rf.get("/") request.shop = get_default_shop() apply_request_middleware(request) request.customer = customer category = get_default_category() category.status = CategoryStatus.VISIBLE category.visibility = CategoryVisibility.VISIBLE_TO_GROUPS category.visibility_groups.add(group) category.shops.add(shop) category.save() vars = {"request": request} context = get_jinja_context(**vars) plugin = CategoryLinksPlugin({"categories": [category.pk], "show_all_categories": show_all_categories}) assert category.is_visible(customer) assert category in plugin.get_context_data(context)["categories"] customer_without_groups = create_random_person() customer_without_groups.groups.clear() assert not category.is_visible(customer_without_groups) request.customer = customer_without_groups context = get_jinja_context(**vars) assert category not in plugin.get_context_data(context)["categories"]
def test_edit_view_adding_messages_to_form_group(rf, admin_user): get_default_shop() # obvious prerequisite product = get_default_product() view = ProductEditView.as_view() request = apply_request_middleware(rf.get("/"), user=admin_user) response = view(request, pk=product.pk) response.render() assert 200 <= response.status_code < 300 assert ProductEditView.add_form_errors_as_messages content = force_text(response.content) post = extract_form_fields(BeautifulSoup(content)) post_data = { # Error in the base form part "base-name__en": "", } post.update(post_data) request = apply_request_middleware(rf.post("/", post), user=admin_user) response = view(request, pk=product.pk) errors = response.context_data["form"].errors assert "base" in errors assert "name__en" in errors["base"]
def test_campaign_end_date(rf, admin_user): """ To make things little bit more simple let's use only english as a language. """ with override_settings(LANGUAGES=[("en", "en")]): shop = get_default_shop() old_name = "test_campaign" object = CatalogCampaign.objects.create(name=old_name, active=True, shop=shop) object.save() view = CatalogCampaignEditView.as_view() new_name = "Test Campaign" assert object.name != new_name data = { "base-name": new_name, "base-public_name__en": "Test Campaign", "base-shop": get_default_shop().id, "base-active": True, "base-basket_line_text": "Test campaign activated!", "base-start_datetime": datetime.datetime(year=2016, month=6, day=19), "base-end_datetime": datetime.datetime(year=2016, month=6, day=10) } methods_before = CatalogCampaign.objects.count() # Conditions, effects and effects is tested separately with override_provides("campaign_context_condition", []): with override_provides("campaign_catalog_filter", []): with override_provides("campaign_product_discount_effect_form", []): request = apply_request_middleware(rf.post("/", data=data), user=admin_user) response = view(request, pk=object.pk) assert response.status_code in [200, 302] content = response.render().content.decode("utf-8") assert "Campaign end date can't be before start date." in content assert CatalogCampaign.objects.count() == methods_before assert CatalogCampaign.objects.get(pk=object.pk).name == old_name
def test_theme_without_default_template_dir(): get_default_shop() with override_current_theme_class(ShuupTestingTheme): c = SmartClient() soup = c.soup(reverse("shuup:index")) assert "Simple base for themes to use" not in soup assert "Welcome to test Shuup!" in soup.find("div", {"class": "page-content"}).text
def test_theme_with_default_template_dir(): get_default_shop() with override_current_theme_class(ShuupTestingThemeWithCustomBase): c = SmartClient() soup = c.soup(reverse("shuup:index")) assert "Simple base for themes to use" in soup.find("h1").text assert "Welcome to test Shuup!" in soup.find("h1").text
def test_menu_toggle(browser, admin_user, live_server, settings): get_default_shop() initialize_admin_browser_test(browser, live_server, settings) wait_until_condition(browser, lambda x: x.is_text_present("Welcome!")) wait_until_condition(browser, lambda x: x.is_text_present("Quicklinks")) wait_until_condition(browser, lambda x: x.is_element_present_by_css("#menu-button")) # Close menu try: browser.find_by_css("#menu-button").first.click() except selenium.common.exceptions.TimeoutException as e: browser.find_by_css("#menu-button").first.click() wait_until_condition(browser, lambda x: x.is_element_present_by_css(".desktop-menu-closed")) url = reverse("shuup_admin:order.list") browser.visit("%s%s" % (live_server, url)) wait_until_condition(browser, condition=lambda x: x.is_text_present("Orders")) # Should be closed after page load wait_until_condition(browser, lambda x: x.is_element_present_by_css(".desktop-menu-closed")) # Open menu browser.find_by_css("#menu-button").first.click() wait_until_condition(browser, lambda x: not x.is_element_present_by_css(".desktop-menu-closed")) url = reverse("shuup_admin:shop_product.list") browser.visit("%s%s" % (live_server, url)) wait_until_condition(browser, condition=lambda x: x.is_text_present("Products")) # Should be still open after page load wait_until_condition(browser, lambda x: not x.is_element_present_by_css(".desktop-menu-closed"))
def test_customer_edit_redirects_to_login_if_not_logged_in(): get_default_shop() # Front middleware needs a Shop to exists urls = ["shuup:customer_edit", "shuup:company_edit"] for url in urls: response = SmartClient().get(reverse(url), follow=False) assert response.status_code == 302 # Redirection ("Found") assert resolve_url(settings.LOGIN_URL) in response.url
def test_refund_with_product_restock(): shop = get_default_shop() supplier = get_simple_supplier() product = create_product( "test-sku", shop=get_default_shop(), default_price=10, stock_behavior=StockBehavior.STOCKED ) supplier.adjust_stock(product.id, 5) assert supplier.get_stock_statuses([product.id])[product.id].logical_count == 5 order = create_order_with_product(product, supplier, 2, 200, shop=shop) order.cache_prices() assert supplier.get_stock_statuses([product.id])[product.id].logical_count == 3 # Create a refund with a parent line and quanity with `restock_products` set to False product_line = order.lines.first() order.create_refund([{"line": product_line, "quantity": 1, "restock_products": False}]) assert supplier.get_stock_statuses([product.id])[product.id].logical_count == 3 assert order.lines.last().taxful_price == -product_line.base_unit_price assert order.get_total_unrefunded_amount() == product_line.base_unit_price.amount # Create a refund with a parent line and quanity with `restock_products` set to True product_line = order.lines.first() order.create_refund([{"line": product_line, "quantity": 1, "restock_products": True}]) assert supplier.get_stock_statuses([product.id])[product.id].logical_count == 4 assert not order.taxful_total_price
def test_refunds_with_quantities(): shop = get_default_shop() supplier = get_default_supplier() product = create_product( "test-sku", shop=get_default_shop(), default_price=10, stock_behavior=StockBehavior.STOCKED ) order = create_order_with_product(product, supplier, 3, 200, shop=shop) order.cache_prices() assert not order.lines.refunds() product_line = order.lines.first() refund_amount = Money(100, order.currency) order.create_refund([{"line": product_line, "quantity": 2, "amount": refund_amount}]) assert len(order.lines.refunds()) == 2 quantity_line = order.lines.refunds().filter(quantity=2).first() assert quantity_line amount_line = order.lines.refunds().filter(quantity=1).first() assert amount_line assert quantity_line.taxful_base_unit_price == -product_line.taxful_base_unit_price assert amount_line.taxful_price.amount == -refund_amount
def test_save_cart_errors(rf, regular_user): get_default_shop() request = apply_request_middleware(rf.post("/", { "title": "test" })) response = CartSaveView.as_view()(request) data = json.loads(response.content.decode("utf8")) assert response.status_code == 403, "can't save cart as anonymous user" assert not data["ok"], "can't save cart without title" customer = get_person_contact(regular_user) request = apply_request_middleware(rf.post("/", { "title": "" }), customer=customer, user=regular_user) response = CartSaveView.as_view()(request) data = json.loads(response.content.decode("utf8")) assert response.status_code == 400 assert not data["ok"], "can't save cart without title" request = apply_request_middleware(rf.post("/", { "title": "test" }), customer=customer, user=regular_user) response = CartSaveView.as_view()(request) data = json.loads(response.content.decode("utf8")) assert response.status_code == 400 assert not data["ok"], "can't save an empty cart"
def test_register_api(): get_default_shop() configuration.set(None, "api_permission_FrontUserViewSet", PermissionLevel.PUBLIC_WRITE) client = _get_client() register_data = { "username": "******", "password": "******" } response = client.post("/api/shuup/front/user/", content_type="application/json", data=json.dumps(register_data)) assert response.status_code == status.HTTP_201_CREATED response_json = json.loads(response.content.decode("utf-8")) assert "token" in response_json assert get_user_model().objects.count() == 1 # Try to register with same username register_data = { "username": "******", "password": "******" } response = client.post("/api/shuup/front/user/", content_type="application/json", data=json.dumps(register_data)) assert response.status_code == status.HTTP_400_BAD_REQUEST assert "User already exists" in response.content.decode("utf-8")
def test_order_creator_view_for_customer(rf, admin_user): get_default_shop() contact = create_random_person(locale="en_US", minimum_name_comp_len=5) request = apply_request_middleware(rf.get("/", {"contact_id": contact.id}), user=admin_user) response = OrderEditView.as_view()(request) assert_contains(response, "customerData") # in the config assert_contains(response, "isCompany") # in the config
def test_custom_view_toolbar_buttons(rf, admin_user): factories.get_default_shop() request = apply_request_middleware(rf.get("/"), user=admin_user) list_view_func = ContactGroupPriceDisplayListView.as_view() edit_view_func = ContactGroupPriceDisplayEditView.as_view() with override_provides("contact_group_price_list_toolbar_provider", [ "shuup.testing.modules.mocker.toolbar:ContactGroupPriceDisplayButtonProvider" ]): list_response = list_view_func(request) list_response.render() list_content = list_response.content.decode("utf-8") assert "btn-contact-group-hello" in list_content edit_response = edit_view_func(request) edit_response.render() edit_response = edit_response.content.decode("utf-8") assert "btn-contact-group-hello" not in edit_response # use global provider - all views should have that button with override_provides("admin_toolbar_button_provider", [ "shuup.testing.modules.mocker.toolbar:ContactGroupPriceDisplayButtonProvider" ]): list_response = list_view_func(request) list_response.render() list_content = list_response.content.decode("utf-8") assert "btn-contact-group-hello" in list_content edit_response = edit_view_func(request) edit_response.render() edit_response = edit_response.content.decode("utf-8") assert "btn-contact-group-hello" in edit_response
def test_sorts_and_filter_in_category_edit(rf, admin_user): get_default_shop() cache.clear() activate("en") with override_provides("front_extend_product_list_form", DEFAULT_FORM_MODIFIERS): category = get_default_category() view = CategoryEditView.as_view() assert get_configuration(category=category) == settings.SHUUP_FRONT_DEFAULT_SORT_CONFIGURATION data = { "base-name__en": category.name, "base-status": category.status.value, "base-visibility": category.visibility.value, "base-ordering": category.ordering, "product_list_facets-sort_products_by_name": True, "product_list_facets-sort_products_by_name_ordering": 6, "product_list_facets-sort_products_by_price": False, "product_list_facets-sort_products_by_price_ordering": 32, "product_list_facets-filter_products_by_manufacturer": True, "product_list_facets-filter_products_by_manufacturer_ordering": 1 } request = apply_request_middleware(rf.post("/", data=data), user=admin_user) response = view(request, pk=category.pk) if hasattr(response, "render"): response.render() assert response.status_code in [200, 302] expected_configurations = { "sort_products_by_name": True, "sort_products_by_name_ordering": 6, "sort_products_by_price": False, "sort_products_by_price_ordering": 32, "filter_products_by_manufacturer": True, "filter_products_by_manufacturer_ordering": 1 } assert get_configuration(category=category) == expected_configurations
def test_user_will_be_redirected_to_user_account_page_after_activation(client): """ 1. Register user 2. Dig out the urls from the email 3. Get the url and see where it redirects 4. See that user's email is in content (in input) 5. Check that the url poins to user_account-page """ if "shuup.front.apps.registration" not in settings.INSTALLED_APPS: pytest.skip("shuup.front.apps.registration required in installed apps") if "shuup.front.apps.customer_information" not in settings.INSTALLED_APPS: pytest.skip("shuup.front.apps.customer_information required in installed apps") get_default_shop() response = client.post(reverse("shuup:registration_register"), data={ "username": username, "email": email, "password1": "password", "password2": "password", }, follow=True) body = mail.outbox[-1].body urls = re.findall('http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+', body) response = client.get(urls[0], follow=True) assert email.encode('utf-8') in response.content, 'email should be found from the page.' assert reverse('shuup:customer_edit') == response.request['PATH_INFO'], 'user should be on the account-page.'
def test_register_bad_data(): get_default_shop() client = _get_client() # no username or email register_data = { "password": "******" } response = client.post("/api/shuup/front/user/", content_type="application/json", data=json.dumps(register_data)) assert response.status_code == status.HTTP_400_BAD_REQUEST assert "username and/or email is required" in response.content.decode("utf-8") # invalid email register_data = { "email": "invalidemail", "password": "******" } response = client.post("/api/shuup/front/user/", content_type="application/json", data=json.dumps(register_data)) assert response.status_code == status.HTTP_400_BAD_REQUEST assert "Enter a valid email address" in response.content.decode("utf-8") # no password register_data = { "email": "*****@*****.**", } response = client.post("/api/shuup/front/user/", content_type="application/json", data=json.dumps(register_data)) assert response.status_code == status.HTTP_400_BAD_REQUEST assert "password" in json.loads(response.content.decode("utf-8"))
def test_new_service_providers_type_select(rf, admin_user, sp_model, type_param): """ Test `ServiceProvideEditView`` with different types of ``ServiceProvider`` subclasses. Make sure that view is rendered and creating new object works. To make things little bit more simple let's use only english as an language. """ with override_settings(LANGUAGES=[("en", "en")]): get_default_shop() view = ServiceProviderEditView.as_view() url = "/" if type_param: url += "?type=%s" % type_param soup = get_bs_object_for_view(rf.get(url), view, admin_user) selected_type = soup.find("select", attrs={"id": "id_type"}).find("option", selected=True)["value"] if type_param: assert type_param == selected_type else: assert selected_type in [ "shuup.customcarrier", "shuup.custompaymentprocessor", "shuup_testing.pseudopaymentprocessor" ] if sp_model: name = "Some provider" data = { "type": type_param, "name__en": name, "enabled": True } provider_count = sp_model.objects.count() get_bs_object_for_view(rf.post(url, data=data), view, admin_user) assert sp_model.objects.count() == provider_count + 1
def test_refund_entire_order(): shop = get_default_shop() supplier = get_simple_supplier() product = create_product( "test-sku", shop=get_default_shop(), default_price=10, stock_behavior=StockBehavior.STOCKED ) supplier.adjust_stock(product.id, 5) assert supplier.get_stock_statuses([product.id])[product.id].logical_count == 5 order = create_order_with_product(product, supplier, 2, 200, Decimal("0.1"), shop=shop) order.cache_prices() original_total_price = order.taxful_total_price assert supplier.get_stock_statuses([product.id])[product.id].logical_count == 3 # Create a full refund with `restock_products` set to False order.create_full_refund(restock_products=False) # Confirm the refund was created with correct amount assert order.taxless_total_price.amount.value == 0 assert order.taxful_total_price.amount.value == 0 refund_line = order.lines.order_by("ordering").last() assert refund_line.type == OrderLineType.REFUND assert refund_line.taxful_price == -original_total_price # Make sure stock status didn't change assert supplier.get_stock_statuses([product.id])[product.id].logical_count == 3
def test_nearby_products(admin_user): get_default_shop() supplier = create_simple_supplier("supplier1") # create Apple and its products shop1 = Shop.objects.create(status=ShopStatus.ENABLED) shop1.contact_address = MutableAddress.objects.create( name="Apple Infinite Loop", street="1 Infinite Loop", country="US", city="Cupertino", latitude=37.331667, longitude=-122.030146) shop1.save() product1 = create_product("macbook", shop1, supplier=supplier) product2 = create_product("imac", shop1, supplier=supplier) # create Google and its products shop2 = Shop.objects.create(status=ShopStatus.ENABLED) shop2.contact_address = MutableAddress.objects.create( name="Google", street="1600 Amphitheatre Pkwy", country="US", city="Mountain View", latitude=37.422000, longitude=-122.084024) shop2.save() product3 = create_product("nexus 1", shop2, supplier=supplier) product4 = create_product("nexux 7", shop2, supplier=supplier) my_position_to_apple = 2.982 my_position_to_google = 10.57 # YMCA my_position = (37.328330, -122.063612) # fetch products and their closest shops params = { "lat": my_position[0], "lng": my_position[1], "ordering": "-distance" } request = get_request("/api/shuup/front/products/", admin_user, data=params) response = FrontProductViewSet.as_view({"get": "list"})(request) response.render() assert response.status_code == status.HTTP_200_OK products = json.loads(response.content.decode("utf-8")) assert len(products) == 4 assert products[0]["id"] == product3.id assert products[0]["name"] == product3.name assert products[0][ "closest_shop_distance"] - my_position_to_google < 0.05 # 5 meters of error margin assert products[1]["id"] == product4.id assert products[1]["name"] == product4.name assert products[1][ "closest_shop_distance"] - my_position_to_google < 0.05 # 5 meters of error margin assert products[2]["id"] == product1.id assert products[2]["name"] == product1.name assert products[2][ "closest_shop_distance"] - my_position_to_apple < 0.05 # 5 meters of error margin assert products[3]["id"] == product2.id assert products[3]["name"] == product2.name assert products[3][ "closest_shop_distance"] - my_position_to_apple < 0.05 # 5 meters of error margin
def test_product_summary(): if "shuup.simple_supplier" not in settings.INSTALLED_APPS: pytest.skip("Need shuup.simple_supplier in INSTALLED_APPS") from shuup_tests.simple_supplier.utils import get_simple_supplier shop = get_default_shop() supplier = get_simple_supplier() product = create_product("test-sku", shop=get_default_shop(), default_price=10, stock_behavior=StockBehavior.STOCKED) supplier.adjust_stock(product.id, 5) # Order with 2 unshipped, non-refunded items and a shipping cost order = create_order_with_product(product, supplier, 2, 200, shop=shop) order.cache_prices() product_line = order.lines.first() shipping_line = order.lines.create(type=OrderLineType.SHIPPING, base_unit_price_value=5, quantity=1) # Make sure no invalid entries and check product quantities product_summary = order.get_product_summary() assert all(product_summary.keys()) summary = product_summary[product.id] assert_defaultdict_values(summary, ordered=2, shipped=0, refunded=0, unshipped=2) # Create a shipment for the other item, make sure status changes assert order.shipping_status == ShippingStatus.NOT_SHIPPED assert order.can_create_shipment() order.create_shipment(supplier=supplier, product_quantities={product: 1}) assert order.shipping_status == ShippingStatus.PARTIALLY_SHIPPED order.create_refund([{ "line": shipping_line, "quantity": 1, "amount": Money(5, order.currency), "restock": False }]) product_summary = order.get_product_summary() assert all(product_summary.keys()) summary = product_summary[product.id] assert_defaultdict_values(summary, ordered=2, shipped=1, refunded=0, unshipped=1) # Create a refund for 2 items, we should get no negative values order.create_refund([{ "line": product_line, "quantity": 2, "amount": Money(200, order.currency), "restock": False }]) product_summary = order.get_product_summary() assert all(product_summary.keys()) summary = product_summary[product.id] assert_defaultdict_values(summary, ordered=2, shipped=1, refunded=2, unshipped=0)
def test_refunds_for_discounted_order_lines(): shop = get_default_shop() supplier = get_default_supplier() product = create_product("test-sku", shop=get_default_shop(), default_price=10, stock_behavior=StockBehavior.STOCKED) order = create_order_with_product(product, supplier, 2, 200, shop=shop) discount_line = OrderLine(order_id=order.id, type=OrderLineType.DISCOUNT, quantity=1, discount_amount_value=Decimal("0.54321")) discount_line.save() order.lines.add(discount_line) product_line = order.lines.filter(type=OrderLineType.PRODUCT).first() product_line.discount_amount = TaxfulPrice(100, order.currency) product_line.save() taxful_price_with_discount = product_line.taxful_price order.cache_prices() order.save() assert product_line.base_price == TaxfulPrice(400, order.currency) assert taxful_price_with_discount == TaxfulPrice(300, order.currency) # try to refund only the product line - should fail since this would result in a negative total with pytest.raises(RefundExceedsAmountException): order.create_refund([{ "line": product_line, "quantity": 2, "amount": taxful_price_with_discount.amount }]) # try to refund the product line with a negative amount with pytest.raises(InvalidRefundAmountException): order.create_refund([{ "line": product_line, "quantity": 1, "amount": -taxful_price_with_discount.amount }]) # try to refund the discount line with a positive amount with pytest.raises(InvalidRefundAmountException): order.create_refund([{ "line": discount_line, "quantity": 1, "amount": -discount_line.taxful_price.amount }]) order.create_refund([{ "line": discount_line, "quantity": 1, "amount": discount_line.taxful_price.amount }, { "line": product_line, "quantity": 2, "amount": taxful_price_with_discount.amount }]) assert product_line.max_refundable_amount.value == 0 assert discount_line.max_refundable_amount.value == 0 assert order.taxful_total_price.value == 0 order = create_order_with_product(product, supplier, 2, 200, shop=shop) discount_line = OrderLine(order_id=order.id, type=OrderLineType.DISCOUNT, quantity=1, discount_amount_value=Decimal("0.54321")) discount_line.save() order.lines.add(discount_line) product_line = order.lines.filter(type=OrderLineType.PRODUCT).first() product_line.discount_amount = TaxfulPrice(100, order.currency) product_line.save() order.cache_prices() order.save() order.create_full_refund(restock_products=False) assert order.taxful_total_price.value == 0
def test_refunds(): shop = get_default_shop() supplier = get_default_supplier() product = create_product( "test-sku", shop=get_default_shop(), default_price=10, ) tax_rate = Decimal("0.1") taxless_base_unit_price = shop.create_price(200) order = create_order_with_product(product, supplier, 3, taxless_base_unit_price, tax_rate, shop=shop) order.payment_status = PaymentStatus.DEFERRED order.cache_prices() order.save() assert order.get_total_refunded_amount().value == 0 assert order.get_total_unrefunded_amount( ).value == order.taxful_total_price.value assert not order.can_edit() assert len(order.lines.all()) == 1 product_line = order.lines.first() assert product_line.ordering == 0 assert order.can_create_refund() assert not order.has_refunds() order.create_refund([{ "line": product_line, "quantity": 1, "amount": (product_line.taxful_price.amount / 3) }]) assert len(order.lines.all()) == 2 assert order.lines.last().ordering == 1 assert order.has_refunds() # Confirm the value of the refund assert order.lines.last().taxful_price == -product_line.base_unit_price assert order.lines.last().tax_amount == -( product_line.taxless_base_unit_price * tax_rate).amount # Create a refund with a parent line and amount order.create_refund([{ "line": product_line, "quantity": 1, "amount": product_line.taxful_price.amount / 3 }]) assert len(order.lines.all()) == 3 assert order.lines.last().ordering == 2 assert order.lines.last( ).taxful_price.amount == -taxless_base_unit_price.amount * (1 + tax_rate) assert order.lines.last( ).tax_amount == -taxless_base_unit_price.amount * tax_rate assert order.taxless_total_price.amount == taxless_base_unit_price.amount assert order.taxful_total_price.amount == taxless_base_unit_price.amount * ( 1 + tax_rate) assert order.can_create_refund() # Try to refunding remaining amount without a parent line with pytest.raises(AssertionError): order.create_refund([{"amount": taxless_base_unit_price}]) # refund remaining amount order.create_refund([{ "line": product_line, "quantity": 1, "amount": product_line.taxful_price.amount / 3 }]) assert len(order.lines.all()) == 4 assert order.lines.last().ordering == 3 assert order.lines.last( ).taxful_price.amount == -taxless_base_unit_price.amount * (1 + tax_rate) assert not order.taxful_total_price.amount assert not order.can_create_refund() with pytest.raises(RefundExceedsAmountException): order.create_refund([{ "line": product_line, "quantity": 1, "amount": taxless_base_unit_price.amount }])
def test_order_refunds_with_multiple_suppliers(): shop = get_default_shop() supplier1 = Supplier.objects.create(identifier="1", name="supplier1") supplier1.shops.add(shop) supplier2 = Supplier.objects.create(identifier="2") supplier2.shops.add(shop) supplier3 = Supplier.objects.create(identifier="3", name="s") supplier3.shops.add(shop) product1 = create_product("sku1", shop=shop, default_price=10) shop_product1 = product1.get_shop_instance(shop=shop) shop_product1.suppliers.set([supplier1, supplier2, supplier3]) product2 = create_product("sku2", shop=shop, default_price=10) shop_product2 = product1.get_shop_instance(shop=shop) shop_product2.suppliers.set([supplier1, supplier2]) product3 = create_product("sku3", shop=shop, default_price=10, shipping_mode=ShippingMode.NOT_SHIPPED) shop_product3 = product1.get_shop_instance(shop=shop) shop_product3.suppliers.set([supplier3]) product_quantities = { supplier1: { product1: 5, product2: 6 }, supplier2: { product1: 3, product2: 13 }, supplier3: { product1: 1, product3: 50 } } def get_quantity(supplier, product): return product_quantities[supplier.pk][product.pk] order = create_empty_order(shop=shop) order.full_clean() order.save() for supplier, product_data in six.iteritems(product_quantities): for product, quantity in six.iteritems(product_data): add_product_to_order(order, supplier, product, quantity, 5) # Lines without quantity shouldn't affect refunds other_line = OrderLine(order=order, type=OrderLineType.OTHER, text="This random line for textual information", quantity=0) other_line.save() order.lines.add(other_line) order.cache_prices() order.create_payment(order.taxful_total_price) assert order.is_paid() # All supplier should be able to refund the order assert order.can_create_refund() assert order.can_create_refund(supplier1) assert order.can_create_refund(supplier2) assert order.can_create_refund(supplier3) assert order.get_total_unrefunded_amount(supplier1).value == Decimal( "55") # 11 * 5 assert order.get_total_unrefunded_quantity(supplier1) == Decimal( "11") # 5 x product1 and 6 x product2 with pytest.raises(RefundExceedsAmountException): order.create_refund([{ "line": "amount", "quantity": 1, "amount": order.shop.create_price(60) }], supplier=supplier1) # Supplier 1 refunds the order order.create_refund(_get_refund_data(order, supplier1)) assert order.get_total_refunded_amount(supplier1).value == Decimal( "55") # 11 * 5 assert order.get_total_unrefunded_amount(supplier1).value == Decimal("0") assert not order.can_create_refund(supplier1) assert order.can_create_refund() assert order.can_create_refund(supplier2) assert order.can_create_refund(supplier3) assert order.get_total_unrefunded_amount(supplier2).value == Decimal( "80") # 16 * 5 assert order.get_total_unrefunded_quantity(supplier2) == Decimal( "16") # 3 x product1 and 13 x product2 with pytest.raises(RefundExceedsAmountException): order.create_refund([{ "line": "amount", "quantity": 1, "amount": order.shop.create_price(81) }], supplier=supplier2) # Supplier 2 refunds the order order.create_refund(_get_refund_data(order, supplier2)) assert order.get_total_refunded_amount(supplier2).value == Decimal( "80") # 11 * 5 assert order.get_total_unrefunded_amount(supplier2).value == Decimal("0") assert not order.can_create_refund(supplier1) assert not order.can_create_refund(supplier2) assert order.can_create_refund() assert order.can_create_refund(supplier3) assert order.get_total_unrefunded_amount(supplier3).value == Decimal( "255") # 51 * 5 assert order.get_total_unrefunded_quantity(supplier3) == Decimal( "51") # 3 x product1 and 13 x product2 with override_settings(SHUUP_ALLOW_ARBITRARY_REFUNDS=False): with pytest.raises(RefundArbitraryRefundsNotAllowedException): order.create_refund([{ "line": "amount", "quantity": 1, "amount": order.shop.create_price(200) }], supplier=supplier3) order.create_refund([{ "line": "amount", "quantity": 1, "amount": order.shop.create_price(200) }], supplier=supplier3) assert OrderLine.objects.filter(order=order, supplier=supplier3, type=OrderLineType.REFUND).exists() # Supplier 3 refunds the order order.create_refund(_get_refund_data(order, supplier3)) assert order.get_total_refunded_amount(supplier3).value == Decimal( "255") # 11 * 5 assert order.get_total_unrefunded_amount(supplier3).value == Decimal("0") assert not order.can_create_refund(supplier1) assert not order.can_create_refund(supplier2) assert not order.can_create_refund(supplier3) assert not order.can_create_refund()
def test_order_arbitrary_refunds_with_multiple_suppliers(): shop = get_default_shop() supplier1 = Supplier.objects.create(identifier="1", name="supplier1") supplier1.shops.add(shop) supplier2 = Supplier.objects.create(identifier="2") supplier2.shops.add(shop) supplier3 = Supplier.objects.create(identifier="3", name="s") supplier3.shops.add(shop) product1 = create_product("sku1", shop=shop, default_price=10) shop_product1 = product1.get_shop_instance(shop=shop) shop_product1.suppliers.set([supplier1, supplier2, supplier3]) product2 = create_product("sku2", shop=shop, default_price=10) shop_product2 = product1.get_shop_instance(shop=shop) shop_product2.suppliers.set([supplier1, supplier2]) product3 = create_product("sku3", shop=shop, default_price=10, shipping_mode=ShippingMode.NOT_SHIPPED) shop_product3 = product1.get_shop_instance(shop=shop) shop_product3.suppliers.set([supplier3]) product_quantities = { supplier1: { product1: 5, product2: 6 }, supplier2: { product1: 3, product2: 13 }, supplier3: { product1: 1, product3: 50 } } def get_quantity(supplier, product): return product_quantities[supplier.pk][product.pk] order = create_empty_order(shop=shop) order.full_clean() order.save() for supplier, product_data in six.iteritems(product_quantities): for product, quantity in six.iteritems(product_data): add_product_to_order(order, supplier, product, quantity, 5) # Lines without quantity shouldn't affect refunds other_line = OrderLine(order=order, type=OrderLineType.OTHER, text="This random line for textual information", quantity=0) other_line.save() order.lines.add(other_line) order.cache_prices() order.create_payment(order.taxful_total_price) assert order.is_paid() # All supplier should be able to refund the order assert order.can_create_refund() assert order.can_create_refund(supplier1) assert order.can_create_refund(supplier2) assert order.can_create_refund(supplier3) # Step by step refund lines for supplier1 assert order.can_create_refund() assert order.get_total_unrefunded_amount(supplier1).value == Decimal( "55") # 11 * 5 assert order.get_total_unrefunded_amount().value == Decimal( "390") # 55 + 80 + 255 proudct1_line_for_supplier1 = order.lines.filter(supplier=supplier1, product=product1).first() supplier1_refund_data = [{ "line": proudct1_line_for_supplier1, "quantity": proudct1_line_for_supplier1.quantity, "amount": order.shop.create_price(20).amount, # Line total is 5 * 5 = 25 "restock_products": True }] order.create_refund(supplier1_refund_data) assert order.get_total_unrefunded_amount(supplier1).value == Decimal("35") order.create_refund([{ "line": "amount", "quantity": 1, "amount": order.shop.create_price(30).amount }], supplier=supplier1) assert order.get_total_unrefunded_amount(supplier1).value == Decimal("5") order.create_refund([{ "line": "amount", "quantity": 1, "amount": order.shop.create_price(5).amount }], supplier=supplier1) assert order.get_total_unrefunded_amount(supplier1).value == Decimal("0") assert order.can_create_refund( supplier1) # Some quantity still left to refund proudct2_line_for_supplier1 = order.lines.filter(supplier=supplier1, product=product2).first() supplier1_restock_refund_data = [{ "line": proudct2_line_for_supplier1, "quantity": proudct2_line_for_supplier1.quantity, "amount": order.shop.create_price(0).amount, # Line total is 5 * 5 = 25 "restock_products": True }] order.create_refund(supplier1_restock_refund_data) assert not order.can_create_refund(supplier1) # Step by step refund lines for supplier2 assert order.can_create_refund() assert order.get_total_unrefunded_amount(supplier2).value == Decimal( "80") # 16 * 5 assert order.get_total_unrefunded_amount().value == Decimal( "335") # 80 + 255 proudct2_line_for_supplier2 = order.lines.filter(supplier=supplier2, product=product2).first() supplier2_refund_data = [{ "line": proudct2_line_for_supplier2, "quantity": 10, "amount": order.shop.create_price(50).amount, # Line total is 13 * 5 = 65 "restock_products": True }] order.create_refund(supplier2_refund_data) assert order.get_total_unrefunded_amount(supplier2).value == Decimal("30") order.create_refund([{ "line": "amount", "quantity": 1, "amount": order.shop.create_price(5).amount }], supplier=supplier2) assert order.get_total_unrefunded_amount(supplier2).value == Decimal("25") order.create_refund([{ "line": "amount", "quantity": 1, "amount": order.shop.create_price(25).amount }], supplier=supplier2) assert order.get_total_unrefunded_amount(supplier2).value == Decimal("0") assert order.can_create_refund( supplier2) # Some quantity still left to refund supplier2_restock_refund_data = [{ "line": proudct2_line_for_supplier2, "quantity": 3, "amount": order.shop.create_price(0).amount, # Line total is 5 * 5 = 25 "restock_products": True }] order.create_refund(supplier2_restock_refund_data) proudct1_line_for_supplier2 = order.lines.filter(supplier=supplier2, product=product1).first() supplier1_restock_refund_data = [{ "line": proudct1_line_for_supplier2, "quantity": proudct1_line_for_supplier2.quantity, "amount": order.shop.create_price(0).amount, # Line total is 5 * 5 = 25 "restock_products": True }] order.create_refund(supplier1_restock_refund_data) assert not order.can_create_refund(supplier2) # Step by step refund lines for supplier3 assert order.can_create_refund() assert order.get_total_unrefunded_amount(supplier3).value == Decimal( "255") # 51 * 5 assert order.get_total_unrefunded_amount().value == Decimal("255") # 255 order.create_refund([{ "line": "amount", "quantity": 1, "amount": order.shop.create_price(55).amount }], supplier=supplier3) assert order.get_total_unrefunded_amount(supplier3).value == Decimal("200") proudct3_line_for_supplier3 = order.lines.filter(supplier=supplier3, product=product3).first() supplier3_refund_data = [{ "line": proudct3_line_for_supplier3, "quantity": 50, "amount": order.shop.create_price(200).amount, # Line total is 13 * 5 = 65 "restock_products": True }] order.create_refund(supplier3_refund_data) assert order.get_total_unrefunded_amount(supplier2).value == Decimal("0") assert order.get_total_unrefunded_amount().value == Decimal("0") assert order.can_create_refund( supplier3) # Some quantity still left to refund proudct1_line_for_supplier3 = order.lines.filter(supplier=supplier3, product=product1).first() supplier3_restock_refund_data = [{ "line": proudct1_line_for_supplier3, "quantity": proudct1_line_for_supplier3.quantity, "amount": order.shop.create_price(0).amount, # Line total is 5 * 5 = 25 "restock_products": True }] order.create_refund(supplier3_restock_refund_data) assert not order.can_create_refund(supplier3) assert not order.can_create_refund()
def test_product_price_info(admin_user, prices_include_tax, product_price, discount, tax_rate, taxful_price, taxless_price): shop = get_default_shop() shop.prices_include_tax = prices_include_tax shop.save() customer = create_random_person() group = customer.get_default_group() customer.user = admin_user customer.groups.add(group) customer.save() tax = factories.get_default_tax() tax.rate = Decimal(tax_rate) tax.save() product = create_product("Just-A-Product", shop, default_price=product_price) CgpDiscount.objects.create(product=product, shop=shop, group=group, discount_amount_value=discount) client = _get_client(admin_user) response = client.get("/api/shuup/front/shop_products/", format="json") data = response.data[0] discounted_price = (product_price - discount) price = (discounted_price if prices_include_tax else discounted_price * (1 + tax_rate)) base_price = (product_price if prices_include_tax else (product_price * (1 + tax_rate))) discount_value = (discount if prices_include_tax else (discount * (1 + tax_rate))) price_info = data["price_info"] def money_round(value): return Money(value, shop.currency).as_rounded(2) assert money_round(data["price"]) == money_round(price) assert money_round(price_info['base_price']) == money_round(base_price) assert money_round(price_info['taxful_price']) == money_round(taxful_price) if prices_include_tax: assert 'taxless_price' not in price_info assert 'tax_amount' not in price_info else: assert money_round( price_info['taxless_price']) == money_round(taxless_price) assert money_round(price_info['tax_amount']) == money_round( (product_price - discount) * tax_rate) assert money_round(price_info['price']) == money_round(price) assert money_round( price_info['discount_amount']) == money_round(discount_value) assert money_round( price_info['discount_rate']) == money_round(discount_value / price if discount else 0) assert price_info['is_discounted'] is (True if discount else False)
def test_product_selection_plugin(rf): shop = factories.get_default_shop() category1 = factories.CategoryFactory(status=CategoryStatus.VISIBLE) category2 = factories.CategoryFactory(status=CategoryStatus.VISIBLE) p1 = factories.create_product("p1", shop, factories.get_default_supplier(), "10") p2 = factories.create_product("p2", shop, factories.get_default_supplier(), "20") p3 = factories.create_product("p3", shop, factories.get_default_supplier(), "30") p4 = factories.create_product("p4", shop, factories.get_default_supplier(), "40") p5 = factories.create_product("p5", shop, factories.get_default_supplier(), "50") sp1 = p1.get_shop_instance(shop) sp2 = p2.get_shop_instance(shop) sp3 = p3.get_shop_instance(shop) sp4 = p4.get_shop_instance(shop) sp1.categories.add(category1, category2) sp2.categories.add(category1) sp3.categories.add(category2) sp4.categories.add(category2) # this discount should show products: p1, p2 and p5 discount1 = Discount.objects.create(name="discount1", active=True, start_datetime=now() - timedelta(days=10), end_datetime=now() + timedelta(days=1), product=p5, category=category1) discount1.shops = [shop] # this discount should show products: p1, p3 and p4 discount2 = Discount.objects.create(name="discount2", active=True, start_datetime=now() - timedelta(days=10), end_datetime=now() + timedelta(days=1), category=category2) discount2.shops = [shop] # this discount shouldn't be available for this shop discount3 = Discount.objects.create(name="discount3", active=True, start_datetime=now() - timedelta(days=10), end_datetime=now() + timedelta(days=1), category=category2) context = get_context(rf) # test only discount1 plugin = DiscountedProductsPlugin({ "discounts": [discount1.pk], "count": 10 }) context_products = plugin.get_context_data(context)["products"] assert p1 in context_products assert p2 in context_products assert p3 not in context_products assert p4 not in context_products assert p5 in context_products for status in range(2): if status == 1: discount2.active = False discount2.save() # test only discount2 plugin = DiscountedProductsPlugin({ "discounts": [discount2.pk], "count": 10 }) context_products = plugin.get_context_data(context)["products"] if status == 1: assert context_products == [] else: assert p1 in context_products assert p2 not in context_products assert p3 in context_products assert p4 in context_products assert p5 not in context_products # test discount3 plugin = DiscountedProductsPlugin({ "discounts": [discount3.pk], "count": 10 }) assert plugin.get_context_data(context)["products"] == [] discount2.active = True discount2.save() # test both discount1 and discount2 plugin = DiscountedProductsPlugin({ "discounts": [discount1.pk, discount2.pk], "count": 10 }) context_products = plugin.get_context_data(context)["products"] assert p1 in context_products assert p2 in context_products assert p3 in context_products assert p4 in context_products assert p5 in context_products # test the plugin form with override_current_theme_class(None): theme = get_current_theme(shop) cell = LayoutCell(theme, DiscountedProductsPlugin.identifier, sizes={"md": 8}) lcfg = LayoutCellFormGroup(layout_cell=cell, theme=theme, request=apply_request_middleware( rf.get("/"))) # not valid, products are required assert not lcfg.is_valid() lcfg = LayoutCellFormGroup(data={ "general-cell_width": "8", "general-cell_align": "pull-right", "plugin-discounts": [discount1.pk, discount2.pk], "plugin-count": 6 }, layout_cell=cell, theme=theme, request=apply_request_middleware( rf.get("/"))) assert lcfg.is_valid() lcfg.save() assert cell.config["discounts"] == [discount1.pk, discount2.pk]
def test_variatins_import(rf): filename = "product_sample_import_with_variations.xlsx" if "shuup.simple_supplier" not in settings.INSTALLED_APPS: pytest.skip("Need shuup.simple_supplier in INSTALLED_APPS") from shuup_tests.simple_supplier.utils import get_simple_supplier activate("en") shop = get_default_shop() product_type = get_default_product_type() sales_unit = get_default_sales_unit() tax_class = get_default_tax_class() # Create media _create_random_media_file(shop, "shirt1.jpeg") _create_random_media_file(shop, "shirt2.jpeg") _create_random_media_file(shop, "shirt3.jpeg") path = os.path.join(os.path.dirname(__file__), "data", "product", filename) transformed_data = transform_file(filename.split(".")[1], path) importer = ProductImporter( transformed_data, ProductImporter.get_importer_context(rf.get("/"), shop=shop, language="en")) importer.process_data() assert len(importer.unmatched_fields) == 0 importer.do_import(ImportMode.CREATE_UPDATE) products = importer.new_objects assert len(products) == 42 # 2 parents 20 variations each supplier = Supplier.objects.first() assert supplier and supplier.stock_managed and supplier.module_identifier == "simple_supplier" assert ShopProduct.objects.filter(suppliers=supplier).count() == 42 parent1 = Product.objects.filter(sku=1).first() assert parent1.mode == ProductMode.VARIABLE_VARIATION_PARENT assert parent1.variation_children.all().count() == 20 # Check product names child1 = Product.objects.filter(sku=10).first() assert child1.name == "T-Shirt - Pink - S" child2 = Product.objects.filter(sku=11).first() assert child2.name == "T-Shirt - Pink - XS" # Check stock counts assert supplier.get_stock_status(child1.pk).logical_count == Decimal(5) assert supplier.get_stock_status(child2.pk).logical_count == Decimal(10) parent2 = Product.objects.filter(sku=22).first() assert parent1.mode == ProductMode.VARIABLE_VARIATION_PARENT assert parent1.variation_children.all().count() == 20 # Check product names child1 = Product.objects.filter(sku=38).first() assert child1.name == "Custom T-Shirt - Black - XL" child2 = Product.objects.filter(sku=39).first() assert child2.name == "Custom T-Shirt - Black - L" # Check stock counts assert supplier.get_stock_status(child1.pk).logical_count == Decimal(5) assert supplier.get_stock_status(child2.pk).logical_count == Decimal(10) path = os.path.join(os.path.dirname(__file__), "data", "product", "product_sample_import_with_variations_update.xlsx") transformed_data = transform_file(filename.split(".")[1], path) importer = ProductImporter( transformed_data, ProductImporter.get_importer_context(rf.get("/"), shop=shop, language="en")) importer.process_data() assert len(importer.unmatched_fields) == 0 importer.do_import(ImportMode.CREATE_UPDATE) products = importer.new_objects assert len(products) == 0 updated_products = importer.updated_objects assert len(updated_products) == 4 # Check product names child1 = Product.objects.filter(sku=10).first() assert child1.name == "T-Shirt - Pink - S" child2 = Product.objects.filter(sku=11).first() assert child2.name == "Test" # Check stock counts assert supplier.get_stock_status(child1.pk).logical_count == Decimal(5) assert supplier.get_stock_status(child2.pk).logical_count == Decimal( 20) # Did not add 20 but made the logical to 20 parent2 = Product.objects.filter(sku=22).first() assert parent1.mode == ProductMode.VARIABLE_VARIATION_PARENT assert parent1.variation_children.all().count() == 20 # Check product names child1 = Product.objects.filter(sku=38).first() assert child1.name == "Custom T-Shirt - Black - XL" child2 = Product.objects.filter(sku=39).first() assert child2.name == "Custom T-Shirt - Black - L" # Check stock counts assert supplier.get_stock_status(child1.pk).logical_count == Decimal(15) assert supplier.get_stock_status(child2.pk).logical_count == Decimal(10) # Test file with missing variations path = os.path.join( os.path.dirname(__file__), "data", "product", "product_sample_import_with_variations_missing_variables.xlsx") transformed_data = transform_file(filename.split(".")[1], path) importer = ProductImporter( transformed_data, ProductImporter.get_importer_context(rf.get("/"), shop=shop, language="en")) importer.process_data() assert len(importer.unmatched_fields) == 0 importer.do_import(ImportMode.CREATE_UPDATE) products = importer.new_objects assert len(products) == 0 updated_products = importer.updated_objects assert len(updated_products) == 4 for log_message in importer.log_messages: assert "Parent SKU set for the row, but no variation" in log_message[ "messages"][0] # check that both variation products still looks correct parent1 = Product.objects.filter(sku=1).first() assert parent1.mode == ProductMode.VARIABLE_VARIATION_PARENT assert parent1.variation_children.all().count() == 20 parent2 = Product.objects.filter(sku=22).first() assert parent1.mode == ProductMode.VARIABLE_VARIATION_PARENT assert parent1.variation_children.all().count() == 20
def test_get_best_selling_products(admin_user): shop1 = get_default_shop() shop2 = get_shop(True) person1 = create_random_person() person1.user = admin_user person1.save() supplier = create_simple_supplier("supplier1") client = _get_client(admin_user) # list best selling products response = client.get("/api/shuup/front/shop_products/best_selling/", { "shop": shop2.pk, "limit": 20 }) assert response.status_code == status.HTTP_200_OK products = json.loads(response.content.decode("utf-8")) assert len(products["results"]) == 0 # THIS IS IMPORTANT! cache.clear() products = [ create_product("Standard-%d" % x, supplier=supplier, shop=shop2) for x in range(10) ] # create 1 product with 4 variations parent_product = create_product("ParentProduct1", supplier=supplier, shop=shop2) children = [ create_product("SimpleVarChild-%d" % x, supplier=supplier, shop=shop2) for x in range(4) ] for child in children: child.link_to_parent(parent_product) best_selling = defaultdict(int) # create orders with standard products for p_index in range(len(products)): order = create_empty_order(shop=shop2) order.save() qty = (len(products) - p_index) add_product_to_order(order, supplier, products[p_index], qty, Decimal(1.0)) order.create_shipment_of_all_products() order.status = OrderStatus.objects.get_default_complete() order.save(update_fields=("status", )) best_selling[products[p_index].id] = qty # create orders with variation products - the parent product is counted instead of its children for p_index in range(2): variation = random.choice(children) qty = 5 order = create_empty_order(shop=shop2) order.save() add_product_to_order(order, supplier, variation, qty, Decimal(1.0)) order.create_shipment_of_all_products() order.status = OrderStatus.objects.get_default_complete() order.save(update_fields=("status", )) best_selling[parent_product.id] = best_selling[parent_product.id] + qty # get the top 100 best selling products response = client.get("/api/shuup/front/shop_products/best_selling/", { "shop": shop2.pk, "limit": 100 }) assert response.status_code == status.HTTP_200_OK products = json.loads(response.content.decode("utf-8")) assert len(products["results"]) == len( best_selling) # as we added less then 100, this must be true assert products["next"] is None # check the if all IDS are part of best selling for ix in range(len(products)): assert products["results"][ix]["product_id"] in best_selling.keys() # get the top 5 best selling products (we should get paginated results) response = client.get("/api/shuup/front/shop_products/best_selling/", { "shop": shop2.pk, "limit": 5 }) assert response.status_code == status.HTTP_200_OK products = json.loads(response.content.decode("utf-8")) assert len(products["results"]) == 5 assert products["count"] == len(best_selling) assert products["next"] is not None sorted_best_selling_ids = [ prod[0] for prod in sorted(best_selling.items(), key=lambda prod: -prod[1]) ][:5] # check the if all the 5 best sellers are part of best selling for ix in range(len(products)): assert products["results"][ix]["product_id"] in sorted_best_selling_ids
def test_attr_api(admin_user): activate("en") get_default_shop() client = APIClient() client.force_authenticate(user=admin_user) attr_data = { "identifier": "attr1", "translations": { "en": {"name": "Kilo"} }, "searchable": False, "type": AttributeType.BOOLEAN.value, "visibility_mode": AttributeVisibility.HIDDEN.value } response = client.post("/api/shuup/attribute/", content_type="application/json", data=json.dumps(attr_data)) assert response.status_code == status.HTTP_201_CREATED attr = Attribute.objects.first() assert attr.name == attr_data["translations"]["en"]["name"] assert attr.searchable == attr_data["searchable"] assert attr.type.value == attr_data["type"] assert attr.identifier == attr_data["identifier"] assert attr.visibility_mode.value == attr_data["visibility_mode"] attr_data["translations"]["en"]["name"] = "Pound" attr_data["identifier"] = "attr2" attr_data["searchable"] = True attr_data["type"] = AttributeType.DECIMAL.value attr_data["visibility_mode"] = AttributeVisibility.SEARCHABLE_FIELD.value response = client.put("/api/shuup/attribute/%d/" % attr.id, content_type="application/json", data=json.dumps(attr_data)) assert response.status_code == status.HTTP_200_OK attr = Attribute.objects.first() assert attr.name == attr_data["translations"]["en"]["name"] assert attr.searchable == attr_data["searchable"] assert attr.type.value == attr_data["type"] assert attr.identifier == attr_data["identifier"] assert attr.visibility_mode.value == attr_data["visibility_mode"] response = client.get("/api/shuup/attribute/%d/" % attr.id) assert response.status_code == status.HTTP_200_OK data = json.loads(response.content.decode("utf-8")) assert attr.name == data["translations"]["en"]["name"] assert attr.searchable == attr_data["searchable"] assert attr.type.value == attr_data["type"] assert attr.identifier == attr_data["identifier"] assert attr.visibility_mode.value == attr_data["visibility_mode"] response = client.get("/api/shuup/attribute/") assert response.status_code == status.HTTP_200_OK data = json.loads(response.content.decode("utf-8")) assert attr.searchable == data[0]["searchable"] assert attr.type.value == data[0]["type"] assert attr.identifier == data[0]["identifier"] assert attr.visibility_mode.value == data[0]["visibility_mode"] response = client.delete("/api/shuup/attribute/%d/" % attr.id) assert response.status_code == status.HTTP_204_NO_CONTENT assert Attribute.objects.count() == 0
def test_order_flow_with_multiple_suppliers(): cache.clear() shop = factories.get_default_shop() factories.create_default_order_statuses() factories.get_default_payment_method() factories.get_default_shipping_method() n_orders_pre = Order.objects.count() product = factories.create_product("sku", shop=shop, default_price=30) shop_product = product.get_shop_instance(shop) # Activate show supplier info for front assert ThemeSettings.objects.count() == 1 theme_settings = ThemeSettings.objects.first() theme_settings.update_settings({"show_supplier_info": True}) supplier_data = [ ("Johnny Inc", 30), ("Mike Inc", 10), ("Simon Inc", 20), ] for name, product_price in supplier_data: supplier = Supplier.objects.create(name=name) shop_product.suppliers.add(supplier) SupplierPrice.objects.create(supplier=supplier, shop=shop, product=product, amount_value=product_price) strategy = "shuup.testing.supplier_pricing.supplier_strategy:CheapestSupplierPriceSupplierStrategy" with override_settings(SHUUP_PRICING_MODULE="supplier_pricing", SHUUP_SHOP_PRODUCT_SUPPLIERS_STRATEGY=strategy): # Ok so cheapest price should be default supplier expected_supplier = shop_product.get_supplier() assert expected_supplier.name == "Mike Inc" with override_current_theme_class(ClassicGrayTheme, shop): # Ensure settings is refreshed from DB c = SmartClient() # Case 1: use default supplier _add_to_basket(c, product.pk, 2) order = _complete_checkout(c, n_orders_pre + 1) assert order product_lines = order.lines.products() assert len(product_lines) == 1 assert product_lines[0].supplier.pk == expected_supplier.pk assert product_lines[0].base_unit_price_value == decimal.Decimal("10") # Case 2: force supplier to Johnny Inc johnny_supplier = Supplier.objects.filter(name="Johnny Inc").first() _add_to_basket(c, product.pk, 3, johnny_supplier) order = _complete_checkout(c, n_orders_pre + 2) assert order product_lines = order.lines.products() assert len(product_lines) == 1 assert product_lines[0].supplier.pk == johnny_supplier.pk assert product_lines[0].base_unit_price_value == decimal.Decimal("30") # Case 3: order 2 pcs from Mike and 3 pcs from Simon Inc mike_supplier = Supplier.objects.filter(name="Mike Inc").first() _add_to_basket(c, product.pk, 2, mike_supplier) simon_supplier = Supplier.objects.filter(name="Simon Inc").first() _add_to_basket(c, product.pk, 3, simon_supplier) order = _complete_checkout(c, n_orders_pre + 3) assert order assert order.taxful_total_price_value == decimal.Decimal("80") # Math: 2x10e + 3x20e product_lines = order.lines.products() assert len(product_lines) == 2 mikes_line = [line for line in product_lines if line.supplier.pk == mike_supplier.pk][0] assert mikes_line assert mikes_line.quantity == 2 assert mikes_line.base_unit_price_value == decimal.Decimal("10") simon_line = [line for line in product_lines if line.supplier.pk == simon_supplier.pk][0] assert simon_line assert simon_line.quantity == 3 assert simon_line.base_unit_price_value == decimal.Decimal("20")
def test_adjust_stock(admin_user): get_default_shop() sp = get_default_shop_product() client = _get_client(admin_user) supplier1 = get_simple_supplier() supplier2 = get_default_supplier() # invalid type response = client.post("/api/shuup/supplier/%s/adjust_stock/" % supplier1.pk, format="json", data={ "product": sp.product.pk, "delta": 100, "type": 200 }) assert response.status_code == status.HTTP_400_BAD_REQUEST data = json.loads(response.content.decode("utf-8")) assert "type" in data # invalid supplier response = client.post("/api/shuup/supplier/%s/adjust_stock/" % 100, format="json", data={ "product": sp.product.pk, "delta": 100, "type": StockAdjustmentType.INVENTORY.value }) assert response.status_code == status.HTTP_404_NOT_FOUND # invalid product response = client.post("/api/shuup/supplier/%s/adjust_stock/" % supplier1.pk, format="json", data={ "product": 100, "delta": 100, "type": StockAdjustmentType.INVENTORY.value }) assert response.status_code == status.HTTP_400_BAD_REQUEST data = json.loads(response.content.decode("utf-8")) assert "product" in data # invalid delta response = client.post("/api/shuup/supplier/%s/adjust_stock/" % supplier1.pk, format="json", data={ "product": sp.product.pk, "delta": "not-a-number", "type": StockAdjustmentType.INVENTORY.value }) assert response.status_code == status.HTTP_400_BAD_REQUEST data = json.loads(response.content.decode("utf-8")) assert "delta" in data # adjust stock not implemented response = client.post("/api/shuup/supplier/%s/adjust_stock/" % supplier2.pk, format="json", data={ "product": sp.product.pk, "delta": 100, "type": StockAdjustmentType.INVENTORY.value }) assert response.status_code == status.HTTP_400_BAD_REQUEST # add 100 to inventory response = client.post("/api/shuup/supplier/%s/adjust_stock/" % supplier1.pk, format="json", data={ "product": sp.product.pk, "delta": 100, "type": StockAdjustmentType.RESTOCK.value }) assert response.status_code == status.HTTP_200_OK stock = supplier1.get_stock_status(sp.product.pk) assert stock.logical_count == 100 assert stock.physical_count == 100 # remove the stocks adjustments and calculate the stock again StockAdjustment.objects.all().delete() # update the stock, response = client.post("/api/shuup/supplier/%s/update_stocks/" % supplier1.pk, format="json", data={"products": [sp.product.pk]}) assert response.status_code == status.HTTP_200_OK # everything should be zero stock = supplier1.get_stock_status(sp.product.pk) assert stock.logical_count == 0 assert stock.physical_count == 0
def test_sample_import_all_match(filename): activate("en") shop = get_default_shop() tax_class = get_default_tax_class() product_type = get_default_product_type() sales_unit = get_default_sales_unit() path = os.path.join(os.path.dirname(__file__), "data", "product", filename) if filename == bom_file: import codecs bytes = min(32, os.path.getsize(path)) raw = open(path, 'rb').read(bytes) assert raw.startswith(codecs.BOM_UTF8) transformed_data = transform_file(filename.split(".")[1], path) importer = ProductImporter(transformed_data, shop, "en") importer.process_data() if filename == images_file: assert len(importer.unmatched_fields) == 2 _create_random_media_file(shop, "image1.jpeg") _create_random_media_file(shop, "products/images/image2.jpeg") _create_random_media_file(shop, "products/images/image3.jpeg") _create_random_media_file(shop, "image4.jpeg") _create_random_media_file(shop, "products2/images/image5.jpeg") _create_random_media_file(shop, "product1.jpeg") _create_random_media_file(shop, "products/images/product2.jpeg") _create_random_media_file(shop, "products/images2/product2.jpeg") else: assert len(importer.unmatched_fields) == 0 importer.do_import(ImportMode.CREATE_UPDATE) products = importer.new_objects if filename == images_file: assert len(products) == 3 else: assert len(products) == 2 for product in products: shop_product = product.get_shop_instance(shop) assert shop_product.pk assert shop_product.default_price_value == 150 assert shop_product.default_price == shop.create_price(150) assert product.type == product_type # product type comes from importer defaults assert product.sales_unit == sales_unit if product.pk == 1: assert product.tax_class.pk == 2 # new was created assert product.name == "Product English" assert product.description == "Description English" if filename == images_file: assert product.media.count() == 3 elif product.pk == 2: assert product.tax_class.pk == tax_class.pk # old was found as should assert product.name == "Product 2 English" assert product.description == "Description English 2" if filename == images_file: assert product.media.count() == 2 elif product.pk == 3 and filename == images_file: assert product.media.count() == 3 assert shop_product.primary_category.pk == 1 assert [c.pk for c in shop_product.categories.all()] == [1, 2]
def test_product_variable_variation_link(admin_user): activate("en") client = _get_client(admin_user) shop = get_default_shop() product1 = create_product("product1", shop=shop) product2 = create_product("product2", shop=shop) product3 = create_product("product3", shop=shop) assert product1.mode == ProductMode.NORMAL var = ProductVariationVariable.objects.create(name="Complex Variable", product=product1) ProductVariationVariableValue.objects.create(variable=var, value="Value A") ProductVariationVariableValue.objects.create(variable=var, value="Value B") product1.verify_mode() product1.save() # create the combinations combinations = list(get_all_available_combinations(product1)) hash1 = combinations[0]["hash"] # this must be Complex Variable: Value A hash2 = combinations[1]["hash"] # this must be Complex Variable: Value B # link product1 <- product2 linkage_data = { "product": product2.pk, "hash": hash1, "status": ProductVariationLinkStatus.VISIBLE.value } response = client.post("/api/shuup/product/%d/variable_variation/" % product1.pk, content_type="application/json", data=json.dumps(linkage_data)) assert response.status_code == status.HTTP_201_CREATED # link product1 <- product3 linkage_data = { "product": product3.pk, "hash": hash2, "status": ProductVariationLinkStatus.VISIBLE.value } response = client.post("/api/shuup/product/%d/variable_variation/" % product1.pk, content_type="application/json", data=json.dumps(linkage_data)) assert response.status_code == status.HTTP_201_CREATED result1 = ProductVariationResult.objects.get(combination_hash=hash1) assert result1.product.pk == product1.pk assert result1.status.value == ProductVariationLinkStatus.VISIBLE.value assert result1.result.pk == product2.pk result2 = ProductVariationResult.objects.get(combination_hash=hash2) assert result2.product.pk == product1.pk assert result2.status.value == ProductVariationLinkStatus.VISIBLE.value assert result2.result.pk == product3.pk combinations = list(get_all_available_combinations(product1)) assert combinations[0]["result_product_pk"] == product2.pk assert combinations[1]["result_product_pk"] == product3.pk # update linkage_data = { "product": product2.pk, "hash": hash2, "status": ProductVariationLinkStatus.INVISIBLE.value } response = client.put("/api/shuup/product/%d/variable_variation/" % product1.pk, content_type="application/json", data=json.dumps(linkage_data)) assert response.status_code == status.HTTP_200_OK result2 = ProductVariationResult.objects.get(combination_hash=hash2) assert result2.product.pk == product1.pk assert result2.status.value == ProductVariationLinkStatus.INVISIBLE.value assert result2.result.pk == product2.pk # delete response = client.delete("/api/shuup/product/%d/variable_variation/" % product1.pk, content_type="application/json", data=json.dumps({"hash": hash2})) assert response.status_code == status.HTTP_204_NO_CONTENT assert ProductVariationResult.objects.count() == 1 response = client.delete("/api/shuup/product/%d/variable_variation/" % product1.pk, content_type="application/json", data=json.dumps({"hash": hash1})) assert response.status_code == status.HTTP_204_NO_CONTENT assert ProductVariationResult.objects.count() == 0 # delete something not existent response = client.delete("/api/shuup/product/%d/variable_variation/" % product1.pk, content_type="application/json", data=json.dumps({"hash": hash1})) assert response.status_code == status.HTTP_404_NOT_FOUND # update not existent response = client.put("/api/shuup/product/%d/variable_variation/" % product1.pk, content_type="application/json", data=json.dumps(linkage_data)) assert response.status_code == status.HTTP_404_NOT_FOUND
def test_page_form(rf, admin_user): shop = get_default_shop() request = rf.get("/") request.user = admin_user request.shop = shop view = PageEditView(request=request) form_class = view.get_form_class() form = form_class(**view.get_form_kwargs()) assert not form.is_bound data = get_form_data(form) data.update({ "available_from": "", "available_to": "", "content__en": "", "content__fi": "suomi", "content__ja": "", "identifier": "", "title__en": "", "title__fi": "", "title__ja": "", "url__en": "", "url__fi": "suomi", "url__ja": "", }) form = form_class(**dict(view.get_form_kwargs(), data=data)) form.full_clean() assert "title__fi" in form.errors # We get an error because all of a given language's fields must be filled if any are data["title__fi"] = "suomi" form = form_class(**dict(view.get_form_kwargs(), data=data)) form.full_clean() assert not form.errors page = form.save() assert set(page.get_available_languages()) == { "fi" } # The page should be only in Finnish # Let's edit that page original_url = "errrnglish" data.update({ "title__en": "englaish", "url__en": original_url, "content__en": "ennnn ennnn ennnnnnn-nn-n-n" }) form = form_class(**dict(view.get_form_kwargs(), data=data, instance=page)) form.full_clean() assert not form.errors page = form.save() assert set(page.get_available_languages()) == {"fi", "en"} # English GET # Try to make page a child of itself data.update({"parent": page.pk}) form = form_class(**dict(view.get_form_kwargs(), data=data, instance=page)) form.full_clean() assert form.errors del data["parent"] # add dummy page with simple url, page is in english dummy = create_page(url="test", shop=get_default_shop()) # edit page again and try to set duplicate url data.update({ "title__en": "englaish", "url__en": "test", "content__en": "ennnn ennnn ennnnnnn-nn-n-n" }) form = form_class(**dict(view.get_form_kwargs(), data=data, instance=page)) form.full_clean() assert len(form.errors) == 1 assert "url__en" in form.errors assert form.errors["url__en"].as_data()[0].code == "invalid_url" # it should be possible to change back to the original url data.update({ "title__en": "englaish", "url__en": original_url, "content__en": "ennnn ennnn ennnnnnn-nn-n-n" }) form = form_class(**dict(view.get_form_kwargs(), data=data, instance=page)) form.full_clean() assert not form.errors page = form.save() # add finnish urls, it should not be possible to enter original url data.update({ "title__fi": "englaish", "url__fi": original_url, "content__fi": "ennnn ennnn ennnnnnn-nn-n-n" }) assert data["url__fi"] == data[ "url__en"] # both urls are same, should raise two errors form = form_class(**dict(view.get_form_kwargs(), data=data, instance=page)) form.full_clean() assert len(form.errors) == 1 assert "url__fi" in form.errors error_data = form.errors["url__fi"].as_data() assert error_data[0].code == "invalid_url" assert error_data[1].code == "invalid_unique_url"
def test_edit_button_no_permission(browser, admin_user, live_server, settings): shop = get_default_shop() manager_group = Group.objects.create(name="Managers") manager = create_random_user("en", is_staff=True) manager.username = "******" manager.set_password("password") manager.save() manager.groups.add(manager_group) shop.staff_members.add(manager) # add permissions for Product admin module manager_permissions = set(["dashboard", "Products", "shop_product.new"]) set_permissions_for_group(manager_group, manager_permissions) get_default_product_type() get_default_sales_unit() get_default_tax_class() initialize_admin_browser_test(browser, live_server, settings, username=manager.username) url = reverse("shuup_admin:shop_product.new") browser.visit("%s%s" % (live_server, url)) sku = "testsku" name = "Some product name" price_value = 10 short_description = "short but gold" browser.fill("base-sku", sku) browser.fill("base-name__en", name) browser.fill("base-short_description__en", short_description) browser.fill("shop%s-default_price_value" % shop.pk, price_value) wait_until_appeared( browser, "#id_shop%d-primary_category ~ .quick-add-btn a.btn" % shop.id) click_element( browser, "#id_shop%d-primary_category ~ .quick-add-btn a.btn" % shop.id) wait_until_appeared(browser, "#create-object-iframe") # no permission to add category with browser.get_iframe('create-object-iframe') as iframe: error = "Can't view this page. You do not have the required permissions: category.new" wait_until_condition(iframe, condition=lambda x: x.is_text_present(error)) # close iframe click_element(browser, "#create-object-overlay a.close-btn") # add permission to add category manager_permissions.add("category.new") manager_permissions.add("category.edit") set_permissions_for_group(manager_group, manager_permissions) # click to add category again click_element( browser, "#id_shop%d-primary_category ~ .quick-add-btn a.btn" % shop.id) wait_until_appeared(browser, "#create-object-iframe") # add the category with browser.get_iframe('create-object-iframe') as iframe: assert Category.objects.count() == 0 wait_until_appeared(iframe, "input[name='base-name__en']") iframe.fill("base-name__en", "Test Category") time.sleep( 3 ) # Let's just wait here to the iFrame to open fully (for Chrome and headless) wait_until_appeared(iframe, "button[form='category_form']") click_element(browser, "button[form='category_form']") wait_until_condition(browser, condition=lambda x: Category.objects.count() == 1, timeout=20) assert Category.objects.first().name == "Test Category" # remove the edit category permissions # add permission to add category manager_permissions.remove("category.edit") set_permissions_for_group(manager_group, manager_permissions) # click to edit the button click_element( browser, "#id_shop%d-primary_category ~ .edit-object-btn a.btn" % shop.id) # no permission to edit category with browser.get_iframe('create-object-iframe') as iframe: error = "Can't view this page. You do not have the required permission(s): category.edit" wait_until_condition(iframe, condition=lambda x: x.is_text_present(error)) # close iframe click_element(browser, "#create-object-overlay a.close-btn") manager_permissions.add("category.edit") set_permissions_for_group(manager_group, manager_permissions) click_element( browser, "#id_shop%d-primary_category ~ .edit-object-btn a.btn" % shop.id) wait_until_appeared(browser, "#create-object-iframe") new_cat_name = "Changed Name" with browser.get_iframe('create-object-iframe') as iframe: wait_until_appeared(iframe, "input[name='base-name__en']") iframe.fill("base-name__en", new_cat_name) time.sleep( 3 ) # Let's just wait here to the iFrame to open fully (for Chrome and headless) wait_until_appeared(iframe, "button[form='category_form']") click_element(browser, "button[form='category_form']") wait_until_condition( browser, condition=lambda x: Category.objects.first().name == new_cat_name, timeout=20)
def test_product_variable_variation_api(admin_user): client = _get_client(admin_user) shop = get_default_shop() product = create_product("product", shop=shop) assert product.mode == ProductMode.NORMAL # create variation_data = { "product": product.pk, "translations": { "en": { "name": "Complex Variation Variable" } } } response = client.post("/api/shuup/product_variation_variable/", content_type="application/json", data=json.dumps(variation_data)) assert response.status_code == status.HTTP_201_CREATED product.refresh_from_db() assert product.mode == ProductMode.VARIABLE_VARIATION_PARENT assert ProductVariationVariable.objects.filter( product=product).count() == 1 assert ProductVariationVariableValue.objects.filter( variable__product=product).count() == 0 # fetch response = client.get("/api/shuup/product_variation_variable/%d/" % ProductVariationVariable.objects.first().pk) assert response.status_code == status.HTTP_200_OK variable_data = json.loads(response.content.decode("utf-8")) assert variable_data["translations"]["en"]["name"] == variation_data[ "translations"]["en"]["name"] # update variation_data = { "product": product.pk, "translations": { "en": { "name": "Complex Variation Variable MODIFIED" } } } response = client.put("/api/shuup/product_variation_variable/%d/" % ProductVariationVariable.objects.first().pk, content_type="application/json", data=json.dumps(variation_data)) assert response.status_code == status.HTTP_200_OK product.refresh_from_db() assert product.mode == ProductMode.VARIABLE_VARIATION_PARENT assert ProductVariationVariable.objects.filter( product=product).count() == 1 assert ProductVariationVariableValue.objects.filter( variable__product=product).count() == 0 # list response = client.get("/api/shuup/product_variation_variable/") assert response.status_code == status.HTTP_200_OK variable_data = json.loads(response.content.decode("utf-8")) assert variable_data[0]["translations"]["en"]["name"] == variation_data[ "translations"]["en"]["name"] # delete response = client.delete("/api/shuup/product_variation_variable/%d/" % ProductVariationVariable.objects.first().pk) assert response.status_code == status.HTTP_204_NO_CONTENT assert ProductVariationVariable.objects.count() == 0 product.refresh_from_db() assert product.mode == ProductMode.NORMAL
def test_protected_contact_groups(rf, admin_user, contact): get_default_shop() request = apply_request_middleware(rf.get("/"), user=admin_user) check_for_delete(request, contact().get_default_group(), False)
def test_quick_add(browser, admin_user, live_server, settings): shop = get_default_shop() get_default_product_type() get_default_sales_unit() get_default_tax_class() initialize_admin_browser_test(browser, live_server, settings) url = reverse("shuup_admin:shop_product.new") browser.visit("%s%s" % (live_server, url)) sku = "testsku" name = "Some product name" price_value = 10 short_description = "short but gold" browser.fill("base-sku", sku) browser.fill("base-name__en", name) browser.fill("base-short_description__en", short_description) browser.fill("shop%s-default_price_value" % shop.pk, price_value) wait_until_appeared( browser, "#id_shop%d-primary_category ~ .quick-add-btn a.btn" % shop.id) click_element( browser, "#id_shop%d-primary_category ~ .quick-add-btn a.btn" % shop.id) wait_until_appeared(browser, "#create-object-iframe") with browser.get_iframe('create-object-iframe') as iframe: assert Category.objects.count() == 0 wait_until_appeared(iframe, "input[name='base-name__en']") iframe.fill("base-name__en", "Test Category") time.sleep( 3 ) # Let's just wait here to the iFrame to open fully (for Chrome and headless) wait_until_appeared(iframe, "button[form='category_form']") click_element(browser, "button[form='category_form']") wait_until_condition(browser, condition=lambda x: Category.objects.count() == 1, timeout=20) assert Category.objects.first().name == "Test Category" # click to edit the button click_element( browser, "#id_shop%d-primary_category ~ .edit-object-btn a.btn" % shop.id) with browser.get_iframe('create-object-iframe') as iframe: wait_until_appeared(iframe, "input[name='base-name__en']") new_cat_name = "Changed Name" iframe.fill("base-name__en", new_cat_name) time.sleep( 3 ) # Let's just wait here to the iFrame to open fully (for Chrome and headless) wait_until_appeared(iframe, "button[form='category_form']") click_element(iframe, "button[form='category_form']") wait_until_condition( browser, condition=lambda x: Category.objects.first().name == new_cat_name, timeout=20) click_element(browser, "button[form='product_form']") wait_until_appeared(browser, "div[class='message success']")
def test_notify_on_company_created(regular_user, allow_company_registration): if "shuup.front.apps.customer_information" not in settings.INSTALLED_APPS: pytest.skip( "shuup.front.apps.customer_information required in installed apps") if "shuup.notify" not in settings.INSTALLED_APPS: pytest.skip("shuup.notify required in installed apps") configuration.set(None, "allow_company_registration", allow_company_registration) step = Step(cond_op=StepConditionOperator.NONE, actions=[ AddNotification({ "message": { "constant": "It Works. {{ customer_email }}" }, "message_identifier": { "constant": "company_created" }, }) ], next=StepNext.STOP) script = Script(event_identifier=CompanyAccountCreated.identifier, name="Test Script", enabled=True, shop=get_default_shop()) script.set_steps([step]) script.save() assert not Notification.objects.filter( identifier="company_created").exists() assert get_person_contact(regular_user) assert not get_company_contact(regular_user) client = SmartClient() client.login(username=REGULAR_USER_USERNAME, password=REGULAR_USER_PASSWORD) company_edit_url = reverse("shuup:company_edit") if allow_company_registration: client.soup(company_edit_url) data = _default_company_data() data.update(_default_address_data("billing")) data.update(_default_address_data("shipping")) response, soup = client.response_and_soup(company_edit_url, data, "post") assert response.status_code == 302 assert get_company_contact(regular_user) assert Notification.objects.filter( identifier="company_created").count() == 1 notification = Notification.objects.filter( identifier="company_created").first() assert notification assert data["contact-email"] in notification.message # New save should not add new notifications response, soup = client.response_and_soup(company_edit_url, data, "post") assert response.status_code == 302 assert Notification.objects.filter( identifier="company_created").count() == 1 script.delete() else: response = client.get(company_edit_url) assert response.status_code == 404 assert Notification.objects.filter( identifier="company_created").count() == 0
def test_contact_group_delete_button(rf, admin_user): get_default_shop() request = apply_request_middleware(rf.get("/"), user=admin_user) check_for_delete(request, get_default_customer_group(), True)
def test_sample_import_no_match(rf, stock_managed): filename = "sample_import_nomatch.xlsx" if "shuup.simple_supplier" not in settings.INSTALLED_APPS: pytest.skip("Need shuup.simple_supplier in INSTALLED_APPS") from shuup_tests.simple_supplier.utils import get_simple_supplier activate("en") shop = get_default_shop() tax_class = get_default_tax_class() product_type = get_default_product_type() supplier = get_simple_supplier(stock_managed) sales_unit = get_default_sales_unit() Manufacturer.objects.create(name="manufctr") path = os.path.join(os.path.dirname(__file__), "data", "product", filename) transformed_data = transform_file(filename.split(".")[1], path) importer = ProductImporter( transformed_data, ProductImporter.get_importer_context(rf.get("/"), shop=shop, language="en") ) importer.process_data() assert len(importer.unmatched_fields) == 1 assert "gtiin" in importer.unmatched_fields importer.manually_match("gtiin", "shuup.core.models.Product:gtin") importer.do_remap() assert len(importer.unmatched_fields) == 0 importer.do_import(ImportMode.CREATE_UPDATE) products = importer.new_objects assert len(products) == 2 for product in products: assert product.gtin == "1280x720" shop_product = product.get_shop_instance(shop) assert shop_product.pk assert shop_product.default_price_value == 150 assert shop_product.default_price == shop.create_price(150) assert product.type == product_type # product type comes from importer defaults assert product.sales_unit == sales_unit if product.pk == 1: assert product.tax_class.pk == 2 # new was created assert product.name == "Product English" assert product.description == "Description English" else: assert product.tax_class.pk == tax_class.pk # old was found as should assert product.name == "Product 2 English" assert product.description == "Description English 2" assert shop_product.primary_category.pk == 1 assert [c.pk for c in shop_product.categories.all()] == [1, 2] # stock was not managed since supplier doesn't like that for msg in importer.other_log_messages: assert "please set `Stock Managed` on" in msg supplier.stock_managed = True supplier.save() importer.do_import("create,update") assert len(importer.other_log_messages) == 0 for sa in StockAdjustment.objects.all(): assert sa.product.pk assert sa.delta == 20
def test_remap(rf, admin_user): lang = "en" import_mode = ImportMode.CREATE_UPDATE sku = "123" name = "test" import_path = reverse("shuup_admin:importer.import.new") process_path = reverse("shuup_admin:importer.import_process") get_default_tax_class() get_default_product_type() get_default_sales_unit() activate("en") shop = get_default_shop() shop.staff_members.add(admin_user) client = SmartClient() client.login(username="******", password="******") csv_content = str.encode("sku;name;gtiin\n%s;%s;111" % (sku, name)) # show import view data = { "importer": "product_importer", "shop": shop.pk, "language": lang, "file": SimpleUploadedFile("file.csv", csv_content, content_type="text/csv"), } response = client.post(import_path, data=data) assert response.status_code == 302 query_string = urlparse(response["location"]).query query = parse_qs(query_string) data = { # data with missing `n` "importer": "product_importer", "shop": shop.pk, "language": lang, "n": query.get("n"), } soup = client.soup(process_path, data=data) assert "The following fields must be manually tied" in str(soup) for row in soup.findAll("tr"): first_td = row.find("td") if not first_td: continue tds = row.findAll("td") if len(tds) < 2: continue second_td = tds[1] if first_td.text.startswith("Connection"): # check that sku was connected badges = second_td.findAll("span", {"class": "badge"}) assert len(badges) == 1 assert badges[0].text == "sku" elif first_td.text.startswith("The following fields"): badges = second_td.findAll("span", {"class": "badge"}) assert len(badges) == 2 badge_texts = [] for b in badges: badge_texts.append(b.text) badge_texts.sort() assert badge_texts[0] == "name" assert badge_texts[1] == "sku" data = {} data["import_mode"] = import_mode.value data["remap[gtiin]"] = "Product:gtin" # map gtin process_submit_path = "%s?%s" % (process_path, query_string) response = client.post(process_submit_path, data=data) assert response.status_code == 302 assert Product.objects.count() == 1 product = Product.objects.first() assert product.gtin == "111"
def test_user_create(rf, admin_user): shop = get_default_shop() view_func = UserDetailView.as_view() before_count = get_user_model().objects.count() response = view_func( apply_request_middleware(rf.post( "/", { "username": "******", "email": "*****@*****.**", "first_name": "test", "last_name": "test", "password": "******", "send_confirmation": True }), user=admin_user)) assert response.status_code == 302 assert get_user_model().objects.count() == before_count + 1 last_user = get_user_model().objects.last() assert last_user not in shop.staff_members.all() assert not len(mail.outbox), "mail not sent since user is not staff" response = view_func( apply_request_middleware(rf.post( "/", { "username": "******", "email": "*****@*****.**", "first_name": "test", "last_name": "test", "password": "******", "is_staff": True, "send_confirmation": True }), user=admin_user)) assert response.status_code == 302 assert get_user_model().objects.count() == before_count + 2 last_user = get_user_model().objects.last() assert last_user in shop.staff_members.all() assert len(mail.outbox) == 1, "mail sent" user = get_user_model().objects.create(username=printable_gibberish(20), first_name=printable_gibberish(10), last_name=printable_gibberish(10), password="******", is_staff=True, is_superuser=False) response = view_func( apply_request_middleware(rf.get("/"), user=user, skip_session=True)) assert response.status_code == 200 response.render() assert "Staff status" not in force_text(response.content) assert "Superuser status" not in force_text(response.content) # remove user staff permission view_func = UserChangePermissionsView.as_view() response = view_func(apply_request_middleware(rf.post( "/", {"is_staff": False}), user=admin_user), pk=last_user.id) assert response.status_code == 302 last_user = get_user_model().objects.last() assert last_user not in shop.staff_members.all() # add again view_func = UserChangePermissionsView.as_view() response = view_func(apply_request_middleware(rf.post( "/", {"is_staff": True}), user=admin_user), pk=last_user.id) assert response.status_code == 302 last_user = get_user_model().objects.last() assert last_user in shop.staff_members.all() # create a superuser view_func = UserDetailView.as_view() response = view_func( apply_request_middleware(rf.post( "/", { "username": "******", "email": "*****@*****.**", "first_name": "test", "last_name": "test", "password": "******", "is_staff": True, "is_superuser": True, "send_confirmation": False }), user=admin_user)) assert response.status_code == 302 assert get_user_model().objects.count() == before_count + 4 last_user = get_user_model().objects.last() # superuser shouldn't be added to staff members assert last_user not in shop.staff_members.all() # change the superuser response = view_func(apply_request_middleware(rf.post( "/", { "username": "******", "email": "*****@*****.**", "first_name": "test2", "last_name": "test", "password": "******", "is_staff": True, "is_superuser": True, }), user=admin_user), pk=last_user.pk) assert response.status_code == 302 assert get_user_model().objects.count() == before_count + 4 last_user = get_user_model().objects.last() # superuser shouldn't be added to staff members assert last_user not in shop.staff_members.all()
def test_product_edit_view(rf, admin_user, settings): shop = get_default_shop() # obvious prerequisite shop.staff_members.add(admin_user) parent = create_product("ComplexVarParent", shop=shop, supplier=get_default_supplier()) sizes = [("%sL" % ("X" * x)) for x in range(4)] for size in sizes: child = create_product("ComplexVarChild-%s" % size, shop=shop, supplier=get_default_supplier()) child.link_to_parent(parent, variables={"size": size}) shop_product = parent.get_shop_instance(shop) cat = CategoryFactory() assert not shop_product.categories.exists() assert not shop_product.primary_category view = ProductEditView.as_view() request = apply_request_middleware(rf.get("/"), user=admin_user) response = view(request, pk=shop_product.pk) response.render() content = force_text(response.content) post = extract_form_fields(BeautifulSoup(content, "lxml")) # Needed for Django 1.8 tests to pass post.update({ 'shop1-default_price_value': '42', 'images-TOTAL_FORMS': '0', 'media-TOTAL_FORMS': '0', 'base-name__fi': 'test', 'base-name__it': 'test', 'base-name__ja': 'test', 'base-name__pt-br': 'test', 'base-name__zh-hans': 'test', }) post_data = {'shop1-primary_category': [], 'shop1-categories': []} post.update(post_data) request = apply_request_middleware(rf.post("/", post), user=admin_user) response = view(request, pk=shop_product.pk) shop_product.refresh_from_db() assert not shop_product.categories.exists() assert not shop_product.primary_category post_data = { 'shop1-default_price_value': 12, 'shop1-primary_category': cat.pk, 'shop1-categories': [] } post.update(post_data) usable_post = {} for k, v in six.iteritems(post): if not k: continue if not post[k]: continue usable_post[k] = v request = apply_request_middleware(rf.post("/", usable_post), user=admin_user) response = view(request, pk=shop_product.pk) shop_product = ShopProduct.objects.first() assert shop_product.primary_category if settings.SHUUP_AUTO_SHOP_PRODUCT_CATEGORIES: assert shop_product.categories.count() == 1 assert shop_product.categories.first() == cat else: assert not shop_product.categories.count() assert shop_product.primary_category == cat post_data = {'shop1-primary_category': [], 'shop1-categories': []} usable_post.update(post_data) request = apply_request_middleware(rf.post("/", usable_post), user=admin_user) response = view(request, pk=shop_product.pk) # empty again shop_product = ShopProduct.objects.first() assert not shop_product.categories.exists() assert not shop_product.primary_category post_data = {'shop1-primary_category': [], 'shop1-categories': [cat.pk]} usable_post.update(post_data) request = apply_request_middleware(rf.post("/", usable_post), user=admin_user) response = view(request, pk=shop_product.pk) shop_product = ShopProduct.objects.first() assert shop_product.categories.count() == 1 assert shop_product.categories.first() == cat if settings.SHUUP_AUTO_SHOP_PRODUCT_CATEGORIES: assert shop_product.primary_category == cat else: assert not shop_product.primary_category cat2 = CategoryFactory() post_data = { 'shop1-primary_category': [], 'shop1-categories': [cat.pk, cat2.pk] } usable_post.update(post_data) request = apply_request_middleware(rf.post("/", usable_post), user=admin_user) response = view(request, pk=shop_product.pk) shop_product = ShopProduct.objects.first() assert shop_product.categories.count() == 2 assert cat in shop_product.categories.all() assert cat2 in shop_product.categories.all() if settings.SHUUP_AUTO_SHOP_PRODUCT_CATEGORIES: assert shop_product.primary_category == cat else: assert not shop_product.primary_category
def test_consolidate_objects(rf, admin_user): shop = get_default_shop() # just visit to make sure GET is ok request = apply_request_middleware(rf.get("/"), user=admin_user) response = ConsolidateSampleObjectsView.as_view()(request) assert response.status_code == 200 def populate_samples(): manager.clear_installed_samples(shop) categories = [ CategoryFactory().pk, CategoryFactory().pk, CategoryFactory().pk ] products = [ ProductFactory().pk, ProductFactory().pk, ProductFactory().pk, ProductFactory().pk ] carousel = Carousel.objects.create(name="crazy stuff").pk manager.save_categories(shop, categories) manager.save_products(shop, products) manager.save_carousel(shop, carousel) def clear_objs(): Product.objects.all().delete() Category.objects.all().delete() Carousel.objects.all().delete() # consolidate everything populate_samples() data = {"categories": False, "products": False, "carousel": False} request = apply_request_middleware(rf.post("/", data=data), user=admin_user) response = ConsolidateSampleObjectsView.as_view()(request) assert response.status_code == 302 assert response["Location"] == reverse("shuup_admin:dashboard") assert Category.objects.count() == 3 assert Product.objects.count() == 4 assert Carousel.objects.count() == 1 assert manager.get_installed_business_segment(shop) is None assert manager.get_installed_products(shop) == [] assert manager.get_installed_categories(shop) == [] assert manager.get_installed_carousel(shop) is None # consolidate nothing clear_objs() populate_samples() data = {"products": True, "categories": True, "carousel": True} request = apply_request_middleware(rf.post("/", data=data), user=admin_user) response = ConsolidateSampleObjectsView.as_view()(request) assert response.status_code == 302 assert response["Location"] == reverse("shuup_admin:dashboard") assert Category.objects.all_except_deleted().count() == 0 assert Product.objects.all_except_deleted().count() == 0 assert Carousel.objects.count() == 0 assert manager.get_installed_business_segment(shop) is None assert manager.get_installed_products(shop) == [] assert manager.get_installed_categories(shop) == [] assert manager.get_installed_carousel(shop) is None # consolidate some clear_objs() populate_samples() data = {"products": False, "categories": False, "carousel": True} request = apply_request_middleware(rf.post("/", data=data), user=admin_user) response = ConsolidateSampleObjectsView.as_view()(request) assert response.status_code == 302 assert response["Location"] == reverse("shuup_admin:dashboard") assert Category.objects.all_except_deleted().count() == 3 assert Product.objects.all_except_deleted().count() == 4 assert Carousel.objects.count() == 0 assert manager.get_installed_business_segment(shop) is None assert manager.get_installed_products(shop) == [] assert manager.get_installed_categories(shop) == [] assert manager.get_installed_carousel(shop) is None
def get_default(request): return get_default_shop()
def test_many_price_info_cache_bump(rf): initial_price = 10 shop = factories.get_default_shop() tax = factories.get_default_tax() tax_class = factories.get_default_tax_class() product = factories.create_product( "product", shop=shop, supplier=factories.get_default_supplier(), default_price=initial_price ) child1 = factories.create_product( "child1", shop=shop, supplier=factories.get_default_supplier(), default_price=5 ) child2 = factories.create_product( "child2", shop=shop, supplier=factories.get_default_supplier(), default_price=9 ) child1.link_to_parent(product, variables={"color": "red"}) child2.link_to_parent(product, variables={"color": "blue"}) request = apply_request_middleware(rf.get("/")) child1_pi = child1.get_price_info(request) child2_pi = child1.get_price_info(request) def assert_cache_products(): cache_many_price_info(request, product, 1, [child1_pi, child2_pi]) assert get_many_cached_price_info(request, product, 1)[0].price == child1_pi.price assert get_many_cached_price_info(request, product, 1)[1].price == child2_pi.price def assert_nothing_is_cached(): # nothing is cached assert get_many_cached_price_info(request, product, 1) is None # cache the item assert_nothing_is_cached() assert_cache_products() # cache bumped - the cache should be dropped - then, cache again tax.save() assert_nothing_is_cached() assert_cache_products() # cache bumped - the cache should be dropped - then, cache again tax_class.save() assert_nothing_is_cached() assert_cache_products() # cache bumped - the cache should be dropped - then, cache again product.save() assert_nothing_is_cached() assert_cache_products() shop_product = product.get_shop_instance(shop) # cache bumped - the cache should be dropped - then, cache again shop_product.save() assert_nothing_is_cached() assert_cache_products() category = factories.get_default_category() # cache bumped - the cache should be dropped - then, cache again shop_product.categories.add(category) assert_nothing_is_cached() assert_cache_products()
def test_product_catalog_campaigns(): shop = get_default_shop() product = create_product("test", shop, default_price=20) shop_product = product.get_shop_instance(shop) cat = Category.objects.create(name="test") campaign = CatalogCampaign.objects.create(shop=shop, name="test", active=True) # no rules assert CatalogCampaign.get_for_product(shop_product).count() == 0 # category filter that doesn't match cat_filter = CategoryFilter.objects.create() cat_filter.categories.add(cat) campaign.filters.add(cat_filter) assert CatalogCampaign.get_for_product(shop_product).count() == 0 shop_product.primary_category = cat shop_product.save() assert CatalogCampaign.get_for_product(shop_product).count() == 1 shop_product.primary_category = None shop_product.save() assert CatalogCampaign.get_for_product(shop_product).count() == 0 # category filter that matches shop_product.categories.add(cat) assert CatalogCampaign.get_for_product(shop_product).count() == 1 # create other shop shop1 = Shop.objects.create(name="testshop", identifier="testshop", status=ShopStatus.ENABLED, public_name="testshop") sp = ShopProduct.objects.create(product=product, shop=shop1, default_price=shop1.create_price(200)) assert product.get_shop_instance(shop1) == sp campaign2 = CatalogCampaign.objects.create(shop=shop1, name="test1", active=True) cat_filter2 = CategoryFilter.objects.create() cat_filter2.categories.add(cat) campaign2.filters.add(cat_filter2) assert CatalogCampaign.get_for_product(sp).count() == 0 # add product to this category sp.primary_category = cat sp.save() assert CatalogCampaign.get_for_product(sp).count() == 1 # matches now sp.primary_category = None sp.save() assert CatalogCampaign.get_for_product(sp).count() == 0 # no match sp.categories.add(cat) assert CatalogCampaign.get_for_product(sp).count() == 1 # matches now campaign3 = CatalogCampaign.objects.create(shop=shop1, name="test1", active=True) cat_filter3 = CategoryFilter.objects.create() cat_filter3.categories.add(cat) campaign3.filters.add(cat_filter3) assert CatalogCampaign.get_for_product( sp).count() == 2 # there are now two matching campaigns in same shop assert CatalogCampaign.get_for_product( shop_product).count() == 1 # another campaign matches only once