def test_user_list(rf, admin_user): shop = get_default_shop() 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 ) shop.staff_members.add(user) view_func = UserListView.as_view() request = rf.get("/", {"jq": json.dumps({"perPage": 100, "page": 1})}) # check with superuser response = view_func(apply_request_middleware(request, user=admin_user)) assert response.status_code == 200 data = json.loads(response.content.decode("utf-8")) assert len(data["items"]) == 2 # check with staff user response = view_func(apply_request_middleware(request, user=user)) assert response.status_code == 200 data = json.loads(response.content.decode("utf-8")) assert len(data["items"]) == 1
def test_mass_edit_products2(rf, admin_user): shop = get_default_shop() supplier = get_default_supplier() product1 = create_product(printable_gibberish(), shop=shop, supplier=supplier, default_price="50") product2 = create_product(printable_gibberish(), shop=shop, supplier=supplier, default_price="501") shop_product1 = product1.get_shop_instance(shop) shop_product2 = product2.get_shop_instance(shop) # ensure no categories set assert shop_product1.primary_category is None assert shop_product2.primary_category is None payload = { "action": InvisibleMassAction().identifier, "values": [product1.pk, product2.pk] } request = apply_request_middleware(rf.post( "/", user=admin_user, )) request._body = json.dumps(payload).encode("UTF-8") view = ProductListView.as_view() response = view(request=request) assert response.status_code == 200 for product in Product.objects.all(): assert product.get_shop_instance(shop).visibility == ShopProductVisibility.NOT_VISIBLE
def test_mass_edit_orders(rf, admin_user): shop = get_default_shop() supplier = get_default_supplier() contact1 = create_random_person() product1 = create_product(printable_gibberish(), shop=shop, supplier=supplier, default_price="50") product2 = create_product(printable_gibberish(), shop=shop, supplier=supplier, default_price="501") order = create_random_order(customer=contact1, products=[product1, product2], completion_probability=0) assert order.status.role != OrderStatusRole.CANCELED payload = { "action": CancelOrderAction().identifier, "values": [order.pk] } request = apply_request_middleware(rf.post( "/", user=admin_user, )) request._body = json.dumps(payload).encode("UTF-8") view = OrderListView.as_view() response = view(request=request) assert response.status_code == 200 for order in Order.objects.all(): assert order.status.role == OrderStatusRole.CANCELED
def get_frontend_order_state(contact, valid_lines=True): """ Get a dict structure mirroring what the frontend JavaScript would submit. :type contact: Contact|None """ translation.activate("en") shop = get_default_shop() tax = Tax.objects.create(code="test_code", rate=decimal.Decimal("0.20"), name="Default") tax_class = TaxClass.objects.create(identifier="test_tax_class", name="Default") rule = TaxRule.objects.create(tax=tax) rule.tax_classes.add(tax_class) rule.save() product = create_product( sku=printable_gibberish(), supplier=get_default_supplier(), shop=shop ) product.tax_class = tax_class product.save() if valid_lines: lines = [ {"id": "x", "type": "product", "product": {"id": product.id}, "quantity": "32", "baseUnitPrice": 50}, {"id": "y", "type": "other", "sku": "hello", "text": "A greeting", "quantity": 1, "unitPrice": "5.5"}, {"id": "z", "type": "text", "text": "This was an order!", "quantity": 0}, ] else: unshopped_product = create_product(sku=printable_gibberish(), supplier=get_default_supplier()) not_visible_product = create_product( sku=printable_gibberish(), supplier=get_default_supplier(), shop=shop ) not_visible_shop_product = not_visible_product.get_shop_instance(shop) not_visible_shop_product.visible = False not_visible_shop_product.save() lines = [ {"id": "x", "type": "product"}, # no product? {"id": "x", "type": "product", "product": {"id": unshopped_product.id}}, # not in this shop? {"id": "y", "type": "product", "product": {"id": -product.id}}, # invalid product? {"id": "z", "type": "other", "quantity": 1, "unitPrice": "q"}, # what's that price? {"id": "rr", "type": "product", "quantity": 1, "product": {"id": not_visible_product.id}} # not visible ] state = { "customer": {"id": contact.id if contact else None}, "lines": lines, "methods": { "shippingMethod": {"id": get_default_shipping_method().id}, "paymentMethod": {"id": get_default_payment_method().id}, }, "shop": { "selected": { "id": shop.id, "name": shop.name, "currency": shop.currency, "priceIncludeTaxes": shop.prices_include_tax } } } return state
def test_basket_shipping_error(rf): StoredBasket.objects.all().delete() shop = get_default_shop() supplier = get_default_supplier() shipped_product = create_product( printable_gibberish(), shop=shop, supplier=supplier, default_price=50, shipping_mode=ShippingMode.SHIPPED ) unshipped_product = create_product( printable_gibberish(), shop=shop, supplier=supplier, default_price=50, shipping_mode=ShippingMode.NOT_SHIPPED ) request = rf.get("/") request.session = {} request.shop = shop apply_request_middleware(request) basket = get_basket(request) # With a shipped product but no shipping methods, we oughta get an error basket.add_product(supplier=supplier, shop=shop, product=shipped_product, quantity=1) assert any(ve.code == "no_common_shipping" for ve in basket.get_validation_errors()) basket.clear_all() # But with an unshipped product, we should not basket.add_product(supplier=supplier, shop=shop, product=unshipped_product, quantity=1) assert not any(ve.code == "no_common_shipping" for ve in basket.get_validation_errors())
def test_mass_edit_orders3(rf, admin_user): shop = get_default_shop() supplier = get_default_supplier() contact1 = create_random_person() product1 = create_product(printable_gibberish(), shop=shop, supplier=supplier, default_price="50") product2 = create_product(printable_gibberish(), shop=shop, supplier=supplier, default_price="501") order1 = create_random_order(customer=contact1, products=[product1, product2], completion_probability=0) order2 = create_random_order(customer=contact1, products=[product1, product2], completion_probability=0) assert order1.status.role != OrderStatusRole.CANCELED assert order2.status.role != OrderStatusRole.CANCELED payload = { "action": OrderConfirmationPdfAction().identifier, "values": [order1.pk, order2.pk] } request = apply_request_middleware(rf.post( "/", user=admin_user, )) request._body = json.dumps(payload).encode("UTF-8") view = OrderListView.as_view() response = view(request=request) assert response.status_code == 200 if weasyprint: assert response['Content-Disposition'] == 'attachment; filename=order_confirmation_pdf.zip' else: assert response["content-type"] == "application/json"
def test_draft_reversion(): view_name = printable_gibberish() theme = ATestTheme() placeholder_name = "test_ph" vc = ViewConfig(theme=theme, view_name=view_name, draft=True) def get_layout_data(draft): # shorthand -- we're going to be doing this a lot in this test case return ViewConfig(theme=theme, view_name=view_name, draft=draft).saved_view_config.get_layout_data(placeholder_name) data1 = {printable_gibberish(): True} data2 = {printable_gibberish(): True} vc.save_placeholder_layout(placeholder_name, data1) vc.publish() assert get_layout_data(draft=False) == data1 assert get_layout_data(draft=True) == data1 vc = ViewConfig(theme=theme, view_name=view_name, draft=True) svc = vc.saved_view_config assert svc.draft assert svc.get_layout_data(placeholder_name) == data1 # Make changes over the last published version svc.set_layout_data(placeholder_name, data2) svc.save() # Still all good in public? assert get_layout_data(draft=False) == data1 # Still got it in draft? assert get_layout_data(draft=True) == data2 # Actually revert those draft changes now! vc.revert() # So in draft we're back to the published version, right? assert get_layout_data(draft=True) == data1
def test_editor_view_commands(): with initialize_editor_view(printable_gibberish(), printable_gibberish()) as view_obj: view_obj.request.method = "POST" view_obj.request.POST = {"command": "add_row"} view_obj._populate_vars() # don't tell anyone we're calling a private method here assert len(view_obj.layout) == 0 view_obj.dispatch(view_obj.request) assert len(view_obj.layout) == 1
def test_move_file(): folder1 = Folder.objects.create(name=printable_gibberish()) folder2 = Folder.objects.create(name=printable_gibberish()) file = File.objects.create(folder=folder1) mbv_command({"action": "move_file", "file_id": file.pk, "folder_id": folder2.pk}) assert File.objects.get(pk=file.pk).folder == folder2 mbv_command({"action": "move_file", "file_id": file.pk, "folder_id": 0}) assert File.objects.get(pk=file.pk).folder is None
def test_get_folder(): folder1 = Folder.objects.create(name=printable_gibberish()) folder2 = Folder.objects.create(name=printable_gibberish(), parent=folder1) root_resp = mbv_command({"action": "folder"}, "GET") assert any(f["id"] == folder1.pk for f in root_resp["folder"]["folders"]) f1_resp = mbv_command({"action": "folder", "id": folder1.pk}, "GET") assert f1_resp["folder"]["folders"][0]["id"] == folder2.pk assert not f1_resp["folder"]["files"] assert f1_resp["folder"]["name"] == folder1.name assert mbv_command({"action": "folder", "id": -1}, "GET")["error"]
def test_basket_free_product(rf): request, shop, _ = initialize_test(rf, False) basket = get_basket(request) supplier = get_default_supplier() single_product_price = "50" original_quantity = 2 # create basket rule that requires 2 products in basket product = create_product(printable_gibberish(), shop=shop, supplier=supplier, default_price=single_product_price) basket.add_product(supplier=supplier, shop=shop, product=product, quantity=2) basket.shipping_method = get_shipping_method(shop=shop) basket.save() second_product = create_product(printable_gibberish(), shop=shop, supplier=supplier, default_price=single_product_price) # no shop third_product = create_product(printable_gibberish(), supplier=supplier) rule = BasketTotalProductAmountCondition.objects.create(value="2") campaign = BasketCampaign.objects.create(active=True, shop=shop, name="test", public_name="test") campaign.conditions.add(rule) effect = FreeProductLine.objects.create(campaign=campaign, quantity=2) effect.products.add(second_product) discount_lines_count = len(effect.get_discount_lines(basket, [], supplier)) assert discount_lines_count == 1 # do not affect as there is no shop product for the product effect.products.add(third_product) assert len(effect.get_discount_lines(basket, [], supplier)) == discount_lines_count basket.uncache() final_lines = basket.get_final_lines() assert len(final_lines) == 3 line_types = [l.type for l in final_lines] assert OrderLineType.DISCOUNT not in line_types for line in basket.get_final_lines(): assert line.type in [OrderLineType.PRODUCT, OrderLineType.SHIPPING] if line.type == OrderLineType.SHIPPING: continue if line.product != product: assert line.product == second_product assert line.line_source == LineSource.DISCOUNT_MODULE assert line.quantity == original_quantity else: assert line.line_source == LineSource.CUSTOMER
def test_deleting_mid_folder(rf): folder1 = Folder.objects.create(name=printable_gibberish()) folder2 = Folder.objects.create(name=printable_gibberish(), parent=folder1) folder3 = Folder.objects.create(name=printable_gibberish(), parent=folder2) tree = get_id_tree(mbv_command({"action": "folders"}, "GET")) assert tree[folder1.pk] == {folder2.pk: {folder3.pk: {}}} mbv_command({"action": "delete_folder", "id": folder2.pk}) tree = get_id_tree(mbv_command({"action": "folders"}, "GET")) assert tree[folder1.pk] == {folder3.pk: {}} folder1 = Folder.objects.get(pk=folder1.pk) assert list(folder1.get_children()) == [folder3]
def test_get_folders(rf): # Create a structure and retrieve it folder1 = Folder.objects.create(name=printable_gibberish()) folder2 = Folder.objects.create(name=printable_gibberish()) folder3 = Folder.objects.create(name=printable_gibberish()) folder4 = Folder.objects.create(name=printable_gibberish(), parent=folder2) folder5 = Folder.objects.create(name=printable_gibberish(), parent=folder3) tree = get_id_tree(mbv_command({"action": "folders"}, "GET")) assert set((folder1.id, folder2.id, folder3.id)) <= set(tree.keys()) assert folder4.pk in tree[folder2.pk] assert folder5.pk in tree[folder3.pk]
def test_variable_variation_form(): var1 = printable_gibberish() var2 = printable_gibberish() parent = create_product(printable_gibberish()) for a in range(4): for b in range(3): child = create_product(printable_gibberish()) child.link_to_parent(parent, variables={var1: a, var2: b}) assert parent.variation_children.count() == 4 * 3 form = VariableVariationChildrenForm(parent_product=parent) assert len(form.fields) == 12
def test_undiscounted_effects(rf, include_tax): request, shop, _ = initialize_test(rf, include_tax) basket = get_basket(request) supplier = get_default_supplier() single_product_price = Decimal(50) discounted_product_quantity = 4 normal_priced_product_quantity = 2 discount_percentage = Decimal(0.2) # 20% discount_amount = basket.create_price(single_product_price * normal_priced_product_quantity * discount_percentage) category = CategoryFactory() discounted_product = create_product( printable_gibberish(), shop=shop, supplier=supplier, default_price=single_product_price) second_product = create_product( printable_gibberish(), shop=shop, supplier=supplier, default_price=single_product_price) ShopProduct.objects.get(shop=shop, product=discounted_product).categories.add(category) ShopProduct.objects.get(shop=shop, product=second_product).categories.add(category) basket.add_product(supplier=supplier, shop=shop, product=discounted_product, quantity=discounted_product_quantity) basket.add_product(supplier=supplier, shop=shop, product=second_product, quantity=normal_priced_product_quantity) basket.shipping_method = get_shipping_method(shop=shop) basket.save() # Store basket price before any campaigns exists original_price = basket.total_price # CatalogCampaign catalog_campaign = CatalogCampaign.objects.create(active=True, shop=shop, name="test", public_name="test") # Limit catalog campaign to "discounted_product" product_filter = ProductFilter.objects.create() product_filter.products.add(discounted_product) catalog_campaign.filters.add(product_filter) # BasketCampaign campaign = BasketCampaign.objects.create( active=True, shop=shop, name="test2", public_name="test2") final_lines = basket.get_final_lines() assert len(final_lines) == 3 # Discount based on undiscounted product values DiscountPercentageFromUndiscounted.objects.create(campaign=campaign, discount_percentage=discount_percentage) basket.uncache() final_lines = basket.get_final_lines() assert len(final_lines) == 4 discounted_basket_price = original_price - discount_amount assert basket.total_price.as_rounded() == discounted_basket_price.as_rounded()
def test_filter_caching(rf): request, shop, group = initialize_test(rf, False) price = shop.create_price product_price = "100" discount_percentage = "0.30" supplier = get_default_supplier() product = create_product(printable_gibberish(), shop=shop, supplier=supplier, default_price=product_price) product2 = create_product(printable_gibberish(), shop=shop, supplier=supplier, default_price=product_price) assert product.pk != product2.pk # ensure they're different # create catalog campaign catalog_filter = ProductFilter.objects.create() catalog_filter.products.add(product) catalog_campaign = CatalogCampaign.objects.create(shop=shop, active=True, name="test") catalog_campaign.filters.add(catalog_filter) assert CatalogFilterCachedShopProduct.objects.count() == 1 catalog_campaign.save() assert CatalogFilterCachedShopProduct.objects.count() == 1 entry = CatalogFilterCachedShopProduct.objects.first() assert entry.pk == get_matching_catalog_filters(product.get_shop_instance(shop))[0] # create another campaign catalog_filter2 = ProductFilter.objects.create() catalog_filter2.products.add(product2) catalog_campaign2 = CatalogCampaign.objects.create(shop=shop, active=True, name="test") catalog_campaign2.filters.add(catalog_filter2) assert CatalogFilterCachedShopProduct.objects.count() == 2 catalog_campaign2.save() assert CatalogFilterCachedShopProduct.objects.count() == 2 # new cache for this product was created entry = CatalogFilterCachedShopProduct.objects.last() assert entry.pk == get_matching_catalog_filters(product2.get_shop_instance(shop))[0] # third campaign catalog_filter3 = ProductFilter.objects.create() catalog_filter3.products.add(product2) catalog_campaign3 = CatalogCampaign.objects.create(shop=shop, active=True, name="test") catalog_campaign3.filters.add(catalog_filter3) assert CatalogFilterCachedShopProduct.objects.count() == 3 catalog_campaign3.save() assert CatalogFilterCachedShopProduct.objects.count() == 3 # new one for this filter again expected = get_matching_catalog_filters(product2.get_shop_instance(shop)) for id in expected: assert id in [catalog_filter2.pk, catalog_filter3.pk]
def test_mass_actions(rf, admin_user): shop = get_default_shop() supplier = get_default_supplier() product = create_product(printable_gibberish(), shop=shop, supplier=supplier, default_price=50) product2 = create_product(printable_gibberish(), shop=shop, supplier=supplier, default_price=50) ids = [product.id, product2.id] request = apply_request_middleware(rf.get("/"), user=admin_user) TestPicotableMassAction().process(request, ids) for product in Product.objects.all(): assert product.cost_center == "test" mass_action_response = TestPicotableFileMassAction().process(request, ids) assert mass_action_response["Content-disposition"] == 'attachment; filename="products.csv"'
def test_basket_orderability_change_shop_product(rf): StoredBasket.objects.all().delete() shop = get_default_shop() supplier = get_default_supplier() product = create_product(printable_gibberish(), shop=shop, supplier=supplier, default_price=50) request = rf.get("/") request.session = {} request.shop = shop apply_request_middleware(request) basket = get_basket(request) line = basket.add_product( supplier=supplier, shop=shop, product=product, quantity=1, force_new_line=True, extra={"foo": "foo"} ) assert len(basket.get_lines()) == 1 assert len(basket.get_unorderable_lines()) == 0 product.get_shop_instance(shop).delete() basket.uncache() assert basket.dirty assert len(basket.get_lines()) == 0 assert len(basket.get_unorderable_lines()) == 1
def _get_order_with_coupon(request, initial_status, condition_product_count=1): shop = request.shop basket = get_basket(request) supplier = get_default_supplier() product = create_product(printable_gibberish(), shop=shop, supplier=supplier, default_price="50") basket.add_product(supplier=supplier, shop=shop, product=product, quantity=1) basket.shipping_method = get_shipping_method(shop=shop) # For shippable products dc = Coupon.objects.create(code="TEST", active=True) campaign = BasketCampaign.objects.create( shop=shop, name="test", public_name="test", coupon=dc, active=True ) BasketDiscountAmount.objects.create(discount_amount=shop.create_price("20"), campaign=campaign) rule = BasketTotalProductAmountCondition.objects.create(value=1) campaign.conditions.add(rule) campaign.save() basket.add_code(dc.code) basket.save() basket.status = initial_status creator = OrderCreator(request) order = creator.create_order(basket) assert order.lines.count() == 3 assert OrderLineType.DISCOUNT in [l.type for l in order.lines.all()] return order
def test_all_categories_view(rf, admin_user): shop = get_default_shop() supplier = get_default_supplier() category = get_default_category() product = get_default_product() request = apply_request_middleware(rf.get("/")) _check_product_count(request, 0) shop_product = product.get_shop_instance(shop) shop_product.categories.add(category) _check_product_count(request, 1) # Create few categories for better test results for i in range(10): cat = Category.objects.create(name=printable_gibberish()) cat.shops.add(shop) new_product_count = random.randint(1, 3) + 1 for i in range(1, new_product_count): product = create_product("sku-%s" % i, shop=shop, supplier=supplier, default_price=10) shop_product = product.get_shop_instance(shop) # Add random categories expect default category which we will make # hidden to make sure that products linked to hidden categories are # not listed shop_product.categories = Category.objects.exclude(id=category.pk).order_by("?")[:i] _check_product_count(request, new_product_count) category.status = CategoryStatus.INVISIBLE category.save() _check_product_count(request, new_product_count - 1)
def _add_products_to_basket(basket): shop = get_default_shop() supplier = get_default_supplier() product = create_product(printable_gibberish(), shop=shop, supplier=supplier, default_price=50) basket.add_product(supplier=supplier, shop=shop, product=product, quantity=2) assert basket.product_count, "basket has products" return basket
def test_order_creator_customer_details(rf, admin_user): shop = get_default_shop() contact = create_random_person(locale="en_US", minimum_name_comp_len=5) company = create_random_company() group = get_default_customer_group() contact.groups.add(group) contact.company_memberships.add(company) contact.save() product = create_product(sku=printable_gibberish(), supplier=get_default_supplier(), shop=shop) order = create_random_order(contact, products=[product]) request = apply_request_middleware(rf.get("/", {"command": "customer_details", "id": contact.id}), user=admin_user) response = OrderEditView.as_view()(request) data = json.loads(response.content.decode("utf8")) assert "customer_info" in data assert "order_summary" in data assert "recent_orders" in data assert data["customer_info"]["name"] == contact.full_name assert data["customer_info"]["phone_no"] == contact.phone assert data["customer_info"]["email"] == contact.email assert company.full_name in data["customer_info"]["companies"] assert group.name in data["customer_info"]["groups"] assert data["customer_info"]["merchant_notes"] == contact.merchant_notes assert len(data["order_summary"]) == 1 assert data["order_summary"][0]["year"] == order.order_date.year assert data["order_summary"][0]["total"] == format_money(order.taxful_total_price) assert len(data["recent_orders"]) == 1 assert data["recent_orders"][0]["status"] == order.get_status_display() assert data["recent_orders"][0]["total"] == format_money(order.taxful_total_price) assert data["recent_orders"][0]["payment_status"] == force_text(order.payment_status.label) assert data["recent_orders"][0]["shipment_status"] == force_text(order.shipping_status.label)
def test_product_visibility(rf, admin_user, regular_user): anon_contact = get_person_contact(AnonymousUser()) shop_product = get_default_shop_product() admin_contact = get_person_contact(admin_user) regular_contact = get_person_contact(regular_user) with modify(shop_product.product, deleted=True): # NB: assigning to `product` here works because `get_shop_instance` populates `_product_cache` assert error_exists(shop_product.get_visibility_errors(customer=anon_contact), "product_deleted") assert error_exists(shop_product.get_visibility_errors(customer=admin_contact), "product_deleted") with pytest.raises(ProductNotVisibleProblem): shop_product.raise_if_not_visible(anon_contact) assert not shop_product.is_list_visible() with modify(shop_product, visibility_limit=ProductVisibility.VISIBLE_TO_ALL, visibility=ShopProductVisibility.NOT_VISIBLE): assert error_exists(shop_product.get_visibility_errors(customer=anon_contact), "product_not_visible") assert error_does_not_exist(shop_product.get_visibility_errors(customer=admin_contact), "product_not_visible") assert not shop_product.is_list_visible() with modify(shop_product, visibility_limit=ProductVisibility.VISIBLE_TO_LOGGED_IN, visibility=ShopProductVisibility.ALWAYS_VISIBLE): assert error_exists(shop_product.get_visibility_errors(customer=anon_contact), "product_not_visible_to_anonymous") assert error_does_not_exist(shop_product.get_visibility_errors(customer=admin_contact), "product_not_visible_to_anonymous") customer_group = get_default_customer_group() grouped_user = get_user_model().objects.create_user(username=printable_gibberish(20)) grouped_contact = get_person_contact(grouped_user) with modify(shop_product, visibility_limit=ProductVisibility.VISIBLE_TO_GROUPS, visibility=ShopProductVisibility.ALWAYS_VISIBLE): shop_product.visibility_groups.add(customer_group) customer_group.members.add(grouped_contact) customer_group.members.remove(get_person_contact(regular_user)) assert error_does_not_exist(shop_product.get_visibility_errors(customer=grouped_contact), "product_not_visible_to_group") assert error_does_not_exist(shop_product.get_visibility_errors(customer=admin_contact), "product_not_visible_to_group") assert error_exists(shop_product.get_visibility_errors(customer=regular_contact), "product_not_visible_to_group")
def test_protected_fields(): activate("en") shop = Shop.objects.create( name="testshop", identifier="testshop", status=ShopStatus.ENABLED, public_name="test shop", domain="derp", currency="EUR" ) get_currency("EUR") get_currency("USD") assert shop.name == "testshop" assert shop.currency == "EUR" assert not ConfigurationItem.objects.filter(shop=shop, key="languages").exists() shop_form = ShopBaseForm(instance=shop, languages=settings.LANGUAGES) assert not shop_form._get_protected_fields() # No protected fields just yet, right? data = get_form_data(shop_form, prepared=True) shop_form = ShopBaseForm(data=data, instance=shop, languages=settings.LANGUAGES) _test_cleanliness(shop_form) shop_form.save() # Now let's make it protected! create_product(printable_gibberish(), shop=shop, supplier=get_default_supplier()) order = create_random_order(customer=create_random_person(), shop=shop) assert order.shop == shop # And try again... data["currency"] = "USD" shop_form = ShopBaseForm(data=data, instance=shop, languages=settings.LANGUAGES) assert shop_form._get_protected_fields() # So protected! _test_cleanliness(shop_form) shop = shop_form.save() assert shop.currency == "EUR" # But the shop form ignored the change . . .
def test_product_not_in_normal_mode(): FormSet = formset_factory(PackageChildForm, PackageChildFormSet, extra=5, can_delete=True) parent = create_product(printable_gibberish()) child_1 = create_product(printable_gibberish()) child_1.link_to_parent(parent) child_2 = create_product(printable_gibberish()) parent.verify_mode() assert parent.mode == ProductMode.SIMPLE_VARIATION_PARENT # Trying to create a package from a non-normal mode product with pytest.raises(Problem): formset = FormSet(parent_product=parent) data = dict(get_form_data(formset, True), **{"form-0-child": child_2.pk, "form-0-quantity": 2}) formset = FormSet(parent_product=parent, data=data) formset.save()
def _get_frontend_order_state(shop, contact): tax = Tax.objects.create(code="test_code", rate=decimal.Decimal("0.20"), name="Default") tax_class = TaxClass.objects.create(identifier="test_tax_class", name="Default") rule = TaxRule.objects.create(tax=tax) rule.tax_classes.add(tax_class) rule.save() product = create_product(sku=printable_gibberish(), supplier=get_default_supplier(), shop=shop) product.tax_class = tax_class product.save() lines = [{"id": "x", "type": "product", "product": {"id": product.id}, "quantity": "32", "baseUnitPrice": 50}] state = { "customer": { "id": contact.id if contact else None, "billingAddress": _encode_address(contact.default_billing_address) if contact else {}, "shippingAddress": _encode_address(contact.default_shipping_address) if contact else {}, }, "lines": lines, "methods": { "shippingMethod": {"id": get_shipping_method(shop=shop).id}, "paymentMethod": {"id": get_payment_method(shop=shop).id}, }, "shop": { "selected": { "id": shop.id, "name": shop.safe_translation_getter("name"), "currency": shop.currency, "priceIncludeTaxes": shop.prices_include_tax, } }, } return state
def test_productfilter_works(rf): request, shop, group = initialize_test(rf, False) price = shop.create_price product_price = "100" discount_percentage = "0.30" supplier = get_default_supplier() product = create_product(printable_gibberish(), shop=shop, supplier=supplier, default_price=product_price) shop_product = product.get_shop_instance(shop) # create catalog campaign catalog_filter = ProductFilter.objects.create() catalog_filter.products.add(product) assert catalog_filter.matches(shop_product) catalog_campaign = CatalogCampaign.objects.create(shop=shop, active=True, name="test") catalog_campaign.filters.add(catalog_filter) cdp = ProductDiscountPercentage.objects.create(campaign=catalog_campaign, discount_percentage=discount_percentage) # add product to basket basket = get_basket(request) basket.add_product(supplier=supplier, shop=shop, product=product, quantity=1) basket.shipping_method = get_shipping_method(shop=shop) basket.save() expected_total = price(product_price) - (Decimal(discount_percentage) * price(product_price)) assert basket.total_price == expected_total
def test_percentage_campaign(rf): request, shop, group = initialize_test(rf, False) price = shop.create_price basket = get_basket(request) supplier = get_default_supplier() # create a basket rule that requires at least value of 200 rule = BasketTotalAmountCondition.objects.create(value="200") product_price = "200" discount_percentage = "0.1" expected_discounted_price = price(product_price) - (price(product_price) * Decimal(discount_percentage)) product = create_product(printable_gibberish(), shop=shop, supplier=supplier, default_price=product_price) basket.add_product(supplier=supplier, shop=shop, product=product, quantity=1) campaign = BasketCampaign.objects.create( shop=shop, public_name="test", name="test", active=True) campaign.conditions.add(rule) campaign.save() BasketDiscountPercentage.objects.create(campaign=campaign, discount_percentage=discount_percentage) assert len(basket.get_final_lines()) == 2 assert basket.product_count == 1 assert basket.total_price == expected_discounted_price
def test_only_cheapest_price_is_selected(rf): request, shop, group = initialize_test(rf, False) price = shop.create_price basket = get_basket(request) supplier = get_default_supplier() # create a basket rule that requires atleast value of 200 rule = BasketTotalAmountCondition.objects.create(value="200") product_price = "200" discount1 = "10" discount2 = "20" # should be selected product = create_product(printable_gibberish(), shop=shop, supplier=supplier, default_price=product_price) basket.add_product(supplier=supplier, shop=shop, product=product, quantity=1) campaign1 = BasketCampaign.objects.create(shop=shop, public_name="test", name="test", active=True) campaign1.conditions.add(rule) campaign1.save() BasketDiscountAmount.objects.create(discount_amount=discount1, campaign=campaign1) campaign2 = BasketCampaign.objects.create(shop=shop, public_name="test", name="test", active=True) campaign2.conditions.add(rule) campaign2.save() BasketDiscountAmount.objects.create(discount_amount=discount2, campaign=campaign2) assert len(basket.get_final_lines()) == 2 line_types = [l.type for l in basket.get_final_lines()] assert OrderLineType.DISCOUNT in line_types for line in basket.get_final_lines(): if line.type == OrderLineType.DISCOUNT: assert line.discount_amount == price(discount2)
def test_basket_free_product_coupon(rf): request, shop, _ = initialize_test(rf, False) basket = get_basket(request) supplier = get_default_supplier() single_product_price = "50" discount_amount_value = "10" # create basket rule that requires 2 products in basket product = create_product(printable_gibberish(), shop=shop, supplier=supplier, default_price=single_product_price) basket.add_product(supplier=supplier, shop=shop, product=product, quantity=1) basket.add_product(supplier=supplier, shop=shop, product=product, quantity=1) basket.shipping_method = get_shipping_method(shop=shop) basket.save() second_product = create_product(printable_gibberish(), shop=shop, supplier=supplier, default_price=single_product_price) rule = BasketTotalProductAmountCondition.objects.create(value="2") coupon = Coupon.objects.create(code="TEST", active=True) campaign = BasketCampaign.objects.create(active=True, shop=shop, name="test", public_name="test", coupon=coupon) campaign.conditions.add(rule) effect = FreeProductLine.objects.create(campaign=campaign) effect.products.add(second_product) basket.add_code(coupon.code) basket.uncache() final_lines = basket.get_final_lines() assert len(final_lines) == 3 line_types = [l.type for l in final_lines] assert OrderLineType.DISCOUNT not in line_types for line in basket.get_final_lines(): assert line.type in [OrderLineType.PRODUCT, OrderLineType.SHIPPING] if line.type == OrderLineType.SHIPPING: continue if line.product != product: assert line.product == second_product
def test_user_detail_works_at_all(rf, admin_user): shop = get_default_shop() user = get_user_model().objects.create(username=printable_gibberish(20), first_name=printable_gibberish(10), last_name=printable_gibberish(10), password="******") view_func = UserDetailView.as_view() response = view_func(apply_request_middleware(rf.get("/"), user=admin_user), pk=user.pk) assert response.status_code == 200 response.render() assert force_text(user) in force_text(response.content) response = view_func(apply_request_middleware(rf.post( "/", {"set_is_active": "0"}), user=admin_user), pk=user.pk) assert response.status_code < 500 and not get_user_model().objects.get( pk=user.pk).is_active with pytest.raises(Problem): view_func(apply_request_middleware(rf.post("/", {"set_is_active": "0"}), user=admin_user), pk=admin_user.pk) 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) shop.staff_members.add(user) # non superusers can't see superusers with pytest.raises(Http404): view_func(apply_request_middleware(rf.get("/"), user=user), pk=admin_user.pk)
def test_basket_free_product_coupon(rf): request, shop, group = initialize_test(rf, False) price = shop.create_price basket = get_basket(request) supplier = get_default_supplier() single_product_price = "50" discount_amount_value = "10" # create basket rule that requires 2 products in basket product = create_product(printable_gibberish(), shop=shop, supplier=supplier, default_price=single_product_price) basket.add_product(supplier=supplier, shop=shop, product=product, quantity=1) basket.add_product(supplier=supplier, shop=shop, product=product, quantity=1) basket.shipping_method = get_shipping_method(shop=shop) basket.save() second_product = create_product(printable_gibberish(), shop=shop, supplier=supplier, default_price=single_product_price) rule = BasketTotalProductAmountCondition.objects.create(value="2") coupon = Coupon.objects.create(code="TEST", active=True) campaign = BasketCampaign.objects.create(active=True, shop=shop, name="test", public_name="test", coupon=coupon) campaign.conditions.add(rule) effect = FreeProductLine.objects.create(campaign=campaign) effect.products.add(second_product) basket.add_code(coupon.code) basket.uncache() final_lines = basket.get_final_lines() assert len(final_lines) == 3 line_types = [l.type for l in final_lines] assert OrderLineType.DISCOUNT not in line_types for line in basket.get_final_lines(): assert line.type in [OrderLineType.PRODUCT, OrderLineType.SHIPPING] if line.type == OrderLineType.SHIPPING: continue if line.product != product: assert line.product == second_product
def test_delete_folder(admin_user): shop = get_default_shop() folder = get_or_create_folder(shop, printable_gibberish()) mbv_command(admin_user, {"action": "delete_folder", "id": folder.pk}) assert not Folder.objects.filter(pk=folder.pk).exists()
def test_basket_free_product(rf): request, shop, _ = initialize_test(rf, False) basket = get_basket(request) supplier = get_default_supplier(shop) single_product_price = "50" original_quantity = 2 # create basket rule that requires 2 products in basket product = create_product(printable_gibberish(), shop=shop, supplier=supplier, default_price=single_product_price) basket.add_product(supplier=supplier, shop=shop, product=product, quantity=2) basket.shipping_method = get_shipping_method(shop=shop) basket.save() second_product = create_product(printable_gibberish(), shop=shop, supplier=supplier, default_price=single_product_price) # no shop third_product = create_product(printable_gibberish(), supplier=supplier) rule = BasketTotalProductAmountCondition.objects.create(value="2") campaign = BasketCampaign.objects.create(active=True, shop=shop, name="test", public_name="test") campaign.conditions.add(rule) effect = FreeProductLine.objects.create(campaign=campaign, quantity=2) effect.products.add(second_product) discount_lines_count = len(effect.get_discount_lines(basket, [], supplier)) assert discount_lines_count == 1 # do not affect as there is no shop product for the product effect.products.add(third_product) assert len(effect.get_discount_lines(basket, [], supplier)) == discount_lines_count basket.uncache() final_lines = basket.get_final_lines() assert len(final_lines) == 3 line_types = [l.type for l in final_lines] assert OrderLineType.DISCOUNT not in line_types for line in basket.get_final_lines(): assert line.type in [OrderLineType.PRODUCT, OrderLineType.SHIPPING] if line.type == OrderLineType.SHIPPING: continue if line.product != product: assert line.product == second_product assert line.line_source == LineSource.DISCOUNT_MODULE assert line.quantity == original_quantity else: assert line.line_source == LineSource.CUSTOMER
def test_undiscounted_effects(rf, include_tax): request, shop, _ = initialize_test(rf, include_tax) basket = get_basket(request) supplier = get_default_supplier(shop) single_product_price = Decimal(50) discounted_product_quantity = 4 normal_priced_product_quantity = 2 discount_percentage = Decimal(0.2) # 20% discount_amount = basket.create_price(single_product_price * normal_priced_product_quantity * discount_percentage) category = CategoryFactory() discounted_product = create_product(printable_gibberish(), shop=shop, supplier=supplier, default_price=single_product_price) second_product = create_product(printable_gibberish(), shop=shop, supplier=supplier, default_price=single_product_price) ShopProduct.objects.get( shop=shop, product=discounted_product).categories.add(category) ShopProduct.objects.get(shop=shop, product=second_product).categories.add(category) basket.add_product(supplier=supplier, shop=shop, product=discounted_product, quantity=discounted_product_quantity) basket.add_product(supplier=supplier, shop=shop, product=second_product, quantity=normal_priced_product_quantity) basket.shipping_method = get_shipping_method(shop=shop) basket.save() # Store basket price before any campaigns exists original_price = basket.total_price # CatalogCampaign catalog_campaign = CatalogCampaign.objects.create(active=True, shop=shop, name="test", public_name="test") # Limit catalog campaign to "discounted_product" product_filter = ProductFilter.objects.create() product_filter.products.add(discounted_product) catalog_campaign.filters.add(product_filter) # BasketCampaign campaign = BasketCampaign.objects.create(active=True, shop=shop, name="test2", public_name="test2") final_lines = basket.get_final_lines() assert len(final_lines) == 3 # Discount based on undiscounted product values DiscountPercentageFromUndiscounted.objects.create( campaign=campaign, discount_percentage=discount_percentage) basket.uncache() final_lines = basket.get_final_lines() assert len(final_lines) == 4 discounted_basket_price = original_price - discount_amount assert basket.total_price.as_rounded( ) == discounted_basket_price.as_rounded()
def test_filter_caching(rf): request, shop, group = initialize_test(rf, False) price = shop.create_price product_price = "100" discount_percentage = "0.30" supplier = get_default_supplier() product = create_product(printable_gibberish(), shop=shop, supplier=supplier, default_price=product_price) product2 = create_product(printable_gibberish(), shop=shop, supplier=supplier, default_price=product_price) assert product.pk != product2.pk # ensure they're different # create catalog campaign catalog_filter = ProductFilter.objects.create() catalog_filter.products.add(product) catalog_campaign = CatalogCampaign.objects.create(shop=shop, active=True, name="test") catalog_campaign.filters.add(catalog_filter) assert CatalogFilterCachedShopProduct.objects.count() == 1 catalog_campaign.save() assert CatalogFilterCachedShopProduct.objects.count() == 1 entry = CatalogFilterCachedShopProduct.objects.first() assert entry.pk == get_matching_catalog_filters( product.get_shop_instance(shop))[0] # create another campaign catalog_filter2 = ProductFilter.objects.create() catalog_filter2.products.add(product2) catalog_campaign2 = CatalogCampaign.objects.create(shop=shop, active=True, name="test") catalog_campaign2.filters.add(catalog_filter2) assert CatalogFilterCachedShopProduct.objects.count() == 2 catalog_campaign2.save() assert CatalogFilterCachedShopProduct.objects.count( ) == 2 # new cache for this product was created entry = CatalogFilterCachedShopProduct.objects.last() assert entry.pk == get_matching_catalog_filters( product2.get_shop_instance(shop))[0] # third campaign catalog_filter3 = ProductFilter.objects.create() catalog_filter3.products.add(product2) catalog_campaign3 = CatalogCampaign.objects.create(shop=shop, active=True, name="test") catalog_campaign3.filters.add(catalog_filter3) assert CatalogFilterCachedShopProduct.objects.count() == 3 catalog_campaign3.save() assert CatalogFilterCachedShopProduct.objects.count( ) == 3 # new one for this filter again expected = get_matching_catalog_filters(product2.get_shop_instance(shop)) for id in expected: assert id in [catalog_filter2.pk, catalog_filter3.pk]
def test_order_creator_view_invalid_command(rf, admin_user): get_default_shop() request = apply_request_middleware(rf.get("/", {"command": printable_gibberish()}), user=admin_user) response = OrderEditView.as_view()(request) assert_contains(response, "unknown command", status_code=400)
def test_upload(rf): response = mbv_upload() assert not File.objects.get(pk=response["file"]["id"]).folder folder = Folder.objects.create(name=printable_gibberish()) response = mbv_upload(folder_id=folder.pk) assert File.objects.get(pk=response["file"]["id"]).folder == folder
def test_variation_detail_view(client): shop = get_default_shop() parent = create_product(printable_gibberish(), shop) assert parent.mode == ProductMode.NORMAL volume = ProductVariationVariable.objects.create( identifier='volume', name='volume', product=parent, ordering=0, ) for index, value in enumerate(['1ml', '2ml', '3ml']): ProductVariationVariableValue.objects.create( identifier=value, value=value, variable=volume, ordering=index, ) color = ProductVariationVariable.objects.create( identifier='color', name='color', product=parent, ordering=1, ) for index, value in enumerate(['red', 'green', 'blue']): ProductVariationVariableValue.objects.create( identifier=value, value=value, variable=color, ordering=index, ) child = create_product(printable_gibberish(), shop) values = [volume.values.all()[1], color.values.all()[1]] assert child.link_to_parent(parent, variables={ volume: values[0], color: values[1], }) assert parent.mode == ProductMode.VARIABLE_VARIATION_PARENT variation_url = '{}?variation={}'.format( reverse('shuup:product', kwargs=dict(pk=parent.pk, slug=parent.slug)), child.sku) response = client.get(variation_url + 'BAD_VARIATION_SKU') assert response.status_code == 404 response = client.get(variation_url) assert response.status_code == 200 assert response.context_data['shop_product'] assert response.context_data['selected_variation'] == child assert response.context_data['selected_variation_values'] == [ v.pk for v in values ]
def test_new_folder(): folder1 = Folder.objects.create(name=printable_gibberish()) child_folder_data = mbv_command({"action": "new_folder", "name": "y", "parent": folder1.id})["folder"] assert Folder.objects.get(pk=child_folder_data["id"]).parent == folder1 root_folder_data = mbv_command({"action": "new_folder", "name": "y"})["folder"] assert not Folder.objects.get(pk=root_folder_data["id"]).parent_id
def test_missing_plugin_render(): plugin_id = printable_gibberish() cell = LayoutCell(FauxTheme, plugin_identifier=plugin_id) assert not cell.plugin_class assert not cell.instantiate_plugin() assert ("%s?" % plugin_id) in cell.render(None) # Should render a "whut?" comment
def test_unsaved_vc_reversion(): shop=get_default_shop() vc = ViewConfig(theme=ATestTheme(shop=shop), shop=shop, view_name=printable_gibberish(), draft=True) vc.revert() # No-op, since this has never been saved (but shouldn't crash either)
def test_rename_folder(): folder = Folder.objects.create(name=printable_gibberish()) mbv_command({"action": "rename_folder", "id": folder.pk, "name": "Space"}) assert Folder.objects.get(pk=folder.pk).name == "Space"
def test_variation_activation(rf): shop = get_default_shop() parent = create_product(printable_gibberish(), shop=shop) user = create_random_user(is_superuser=True, is_staff=True) request = apply_request_middleware(rf.get("/"), user=user, shop=shop) form = VariationVariablesDataForm(parent_product=parent, request=request) assert len(ProductVariationVariable.objects.all() ) == 0 # Assert no active Variations are present assert form.get_variation_templates() == [ ] # Assert no templates exist (yet) var_data_dict = { 'data': json.dumps({"variable_values": var_data}), 'activate_template': True, 'template_name': '' } # Base skeleton form.cleaned_data = var_data_dict form.is_valid() form.save() assert len( ProductVariationVariable.objects.filter(product=parent)) == 1 # Size assert len(ProductVariationVariableValue.objects.all()) == 2 # X,S var_data_one_value = [{ "pk": "$0.3", "identifier": "", "names": { "en": "Size" }, "values": [{ "pk": "$0.8", "identifier": "", "texts": { "en": "X" } }] }] # Delete one value var_data_dict['data'] = json.dumps({"variable_values": var_data_one_value}) form.cleaned_data = var_data_dict form.save() assert len(ProductVariationVariable.objects.filter(product=parent)) == 1 assert len(ProductVariationVariableValue.objects.all()) == 1 var_data_dict["template_name"] = printable_gibberish() # Create template form.save() assert len(ProductVariationVariable.objects.filter(product=parent)) == 1 assert len(ProductVariationVariableValue.objects.all()) == 1 assert len(form.get_variation_templates()) == 1 template_identifier = form.get_variation_templates()[0].get('identifier') # dict with template identifier which contains only one ProductVariationVariableValue data one_value_dict = json.dumps({ "variable_values": var_data_one_value, 'template_identifier': template_identifier }) two_values_dict = json.dumps({ "variable_values": var_data, 'template_identifier': template_identifier }) # Has two PVVV var_data_dict['data'] = two_values_dict var_data_dict['template_name'] = '' form.cleaned_data = var_data_dict form.save() assert len(ProductVariationVariable.objects.filter(product=parent)) == 1 assert len(ProductVariationVariableValue.objects.all()) == 2 var_template_data = form.get_variation_templates()[0].get("data") assert var_template_data == var_data var_data_dict['data'] = one_value_dict form.cleaned_data = var_data_dict form.save() assert len(ProductVariationVariable.objects.filter(product=parent)) == 1 assert len(ProductVariationVariableValue.objects.all()) == 1 var_template_data = form.get_variation_templates()[0].get("data") assert var_template_data == var_data_one_value var_data_dict['data'] = two_values_dict var_data_dict['activate_template'] = False form.cleaned_data = var_data_dict form.save() assert len(ProductVariationVariable.objects.filter(product=parent)) == 1 assert len( ProductVariationVariableValue.objects.all() ) == 1 # Assert newest template data hasn't been activated since activated_template = False var_template_data = form.get_variation_templates()[0].get("data") assert var_template_data == var_data
def test_order_create_without_shipping_or_billing_method(admin_user): create_default_order_statuses() shop = get_default_shop() contact = create_random_person(locale="en_US", minimum_name_comp_len=5) product = create_product(sku=printable_gibberish(), supplier=get_default_supplier(), shop=shop) assert not Order.objects.count() client = _get_client(admin_user) lines = [ { "type": "product", "product": product.id, "quantity": "1", "base_unit_price_value": "5.00" }, { "type": "product", "product": product.id, "quantity": "2", "base_unit_price_value": "1.00", "discount_amount_value": "0.50" }, { "type": "other", "sku": "hello", "text": "A greeting", "quantity": 1, "base_unit_price_value": "3.5" }, { "type": "text", "text": "This was an order!", "quantity": 0 }, ] response = client.post("/api/shuup/order/", content_type="application/json", data=json.dumps({ "shop": shop.pk, "customer": contact.pk, "lines": lines })) assert response.status_code == 201 assert Order.objects.count() == 1 order = Order.objects.first() assert order.shop == shop assert order.shipping_method is None assert order.payment_method is None assert order.customer == contact assert order.creator == admin_user assert order.billing_address == contact.default_billing_address.to_immutable( ) assert order.shipping_address == contact.default_shipping_address.to_immutable( ) assert order.payment_status == PaymentStatus.NOT_PAID assert order.shipping_status == ShippingStatus.NOT_SHIPPED assert order.status == OrderStatus.objects.get_default_initial() assert order.taxful_total_price_value == decimal.Decimal(10) assert order.lines.count() == 4 # 2 product lines, 2 other lines for idx, line in enumerate(order.lines.all()[:4]): assert line.quantity == decimal.Decimal(lines[idx].get("quantity")) assert line.base_unit_price_value == decimal.Decimal(lines[idx].get( "base_unit_price_value", 0)) assert line.discount_amount_value == decimal.Decimal(lines[idx].get( "discount_amount_value", 0))
def get_frontend_order_state(contact, valid_lines=True): """ Get a dict structure mirroring what the frontend JavaScript would submit. :type contact: Contact|None """ translation.activate("en") shop = get_default_shop() tax, created = Tax.objects.get_or_create(code="test_code", defaults={ "rate": decimal.Decimal("0.20"), "name": "Default" }) tax_class, created = TaxClass.objects.get_or_create( identifier="test_tax_class", defaults={"name": "Default"}) rule, created = TaxRule.objects.get_or_create(tax=tax) rule.tax_classes.add(tax_class) rule.save() product = create_product(sku=printable_gibberish(), supplier=get_default_supplier(), shop=shop) product.tax_class = tax_class product.save() if valid_lines: lines = [ { "id": "x", "type": "product", "product": { "id": product.id }, "quantity": "32", "baseUnitPrice": 50 }, { "id": "y", "type": "other", "sku": "hello", "text": "A greeting", "quantity": 1, "unitPrice": "5.5" }, { "id": "z", "type": "text", "text": "This was an order!", "quantity": 0 }, ] else: unshopped_product = create_product(sku=printable_gibberish(), supplier=get_default_supplier()) not_visible_product = create_product(sku=printable_gibberish(), supplier=get_default_supplier(), shop=shop) not_visible_shop_product = not_visible_product.get_shop_instance(shop) not_visible_shop_product.visibility = ShopProductVisibility.NOT_VISIBLE not_visible_shop_product.save() lines = [ { "id": "x", "type": "product" }, # no product? { "id": "x", "type": "product", "product": { "id": unshopped_product.id } }, # not in this shop? { "id": "y", "type": "product", "product": { "id": -product.id } }, # invalid product? { "id": "z", "type": "other", "quantity": 1, "unitPrice": "q" }, # what's that price? { "id": "rr", "type": "product", "quantity": 1, "product": { "id": not_visible_product.id } } # not visible ] state = { "customer": { "id": contact.id if contact else None, "billingAddress": encode_address(contact.default_billing_address) if contact else {}, "shippingAddress": encode_address(contact.default_shipping_address) if contact else {}, }, "lines": lines, "methods": { "shippingMethod": { "id": get_default_shipping_method().id }, "paymentMethod": { "id": get_default_payment_method().id }, }, "shop": { "selected": { "id": shop.id, "name": shop.name, "currency": shop.currency, "priceIncludeTaxes": shop.prices_include_tax } } } return state
def test_editor_view_unknown_command(): with initialize_editor_view(printable_gibberish(), printable_gibberish()) as view_obj: view_obj.request.method = "POST" view_obj.request.POST = {"command": printable_gibberish()} with pytest.raises(Problem): view_obj.dispatch(view_obj.request)
def test_basket_campaign_case2(rf): request, shop, group = initialize_test(rf, False) price = shop.create_price basket = get_basket(request) supplier = get_default_supplier(shop) # create a basket rule that requires at least value of 200 rule = BasketTotalAmountCondition.objects.create(value="200") single_product_price = "50" discount_amount_value = "10" unique_shipping_method = get_shipping_method(shop, price=50) for x in range(3): product = create_product(printable_gibberish(), shop=shop, supplier=supplier, default_price=single_product_price) basket.add_product(supplier=supplier, shop=shop, product=product, quantity=1) assert basket.product_count == 3 campaign = BasketCampaign.objects.create(shop=shop, public_name="test", name="test", active=True) campaign.conditions.add(rule) campaign.save() BasketDiscountAmount.objects.create(discount_amount=discount_amount_value, campaign=campaign) assert len(basket.get_final_lines()) == 3 assert basket.total_price == price( single_product_price) * basket.product_count # check that shipping method affects campaign basket.shipping_method = unique_shipping_method basket.save() basket.uncache() assert len(basket.get_final_lines() ) == 4 # Shipping should not affect the rule being triggered line_types = [l.type for l in basket.get_final_lines()] assert OrderLineType.DISCOUNT not in line_types product = create_product(printable_gibberish(), shop=shop, supplier=supplier, default_price=single_product_price) basket.add_product(supplier=supplier, shop=shop, product=product, quantity=1) assert len(basket.get_final_lines()) == 6 # Discount included assert OrderLineType.DISCOUNT in [l.type for l in basket.get_final_lines()]
def test_rename_folder(admin_user): shop = get_default_shop() folder = get_or_create_folder(shop, printable_gibberish()) mbv_command(admin_user, {"action": "rename_folder", "id": folder.pk, "name": "Space"}) assert Folder.objects.get(pk=folder.pk).name == "Space"
def test_multiple_campaigns_match_with_coupon(rf): request, shop, group = initialize_test(rf, False) price = shop.create_price basket = get_basket(request) supplier = get_default_supplier(shop) # create a basket rule that requires atleast value of 200 rule = BasketTotalAmountCondition.objects.create(value="200") product_price = "200" discount1 = "10" discount2 = "20" product = create_product(printable_gibberish(), shop=shop, supplier=supplier, default_price=product_price) basket.add_product(supplier=supplier, shop=shop, product=product, quantity=1) basket.shipping_method = get_shipping_method(shop=shop) campaign = BasketCampaign.objects.create(shop=shop, public_name="test", name="test", active=True) campaign.conditions.add(rule) campaign.save() BasketDiscountAmount.objects.create(discount_amount=discount1, campaign=campaign) dc = Coupon.objects.create(code="TEST", active=True) campaign2 = BasketCampaign.objects.create(shop=shop, public_name="test", name="test", coupon=dc, active=True) BasketDiscountAmount.objects.create(discount_amount=discount2, campaign=campaign2) basket.add_product(supplier=supplier, shop=shop, product=product, quantity=1) resp = handle_add_campaign_code(request, basket, dc.code) assert resp.get("ok") discount_lines_values = [ line.discount_amount for line in basket.get_final_lines() if line.type == OrderLineType.DISCOUNT ] assert price(discount1) in discount_lines_values assert price(discount2) in discount_lines_values assert basket.total_price == (price(product_price) * basket.product_count - price(discount1) - price(discount2)) assert basket.codes == [dc.code] # test code removal resp = handle_remove_campaign_code(request, basket, dc.code) assert resp.get("ok") assert basket.codes == [] discount_lines_values = [ line.discount_amount for line in basket.get_final_lines() if line.type == OrderLineType.DISCOUNT ] assert price(discount1) in discount_lines_values assert not price(discount2) in discount_lines_values
def test_unknown_theme_fails(rf): request = rf.get("/", {"theme": printable_gibberish()}) request.user = SuperUser() request.shop = get_default_shop() with pytest.raises(Problem): EditorView.as_view()(request)
def test_delete_folder(): folder = Folder.objects.create(name=printable_gibberish()) mbv_command({"action": "delete_folder", "id": folder.pk}) assert not Folder.objects.filter(pk=folder.pk).exists()
def test_product_category_discount_amount_with_minimum_price(rf): # Buy X amount of Y get Z discount from Y request, shop, _ = initialize_test(rf, False) basket = get_basket(request) supplier = get_default_supplier(shop) single_product_price = Decimal("50") single_product_min_price = Decimal("40") discount_amount_value = Decimal("200") # will exceed the minimum price quantity = 2 # the expected discount amount should not be greater than the products expected_discount_amount = basket.create_price( single_product_price) * quantity category = CategoryFactory() # create basket rule that requires 2 products in basket product = create_product(printable_gibberish(), shop=shop, supplier=supplier, default_price=single_product_price) shop_product = ShopProduct.objects.get(shop=shop, product=product) shop_product.minimum_price_value = single_product_min_price shop_product.save() shop_product.categories.add(category) basket.add_product(supplier=supplier, shop=shop, product=product, quantity=quantity) basket.shipping_method = get_shipping_method(shop=shop) basket.save() rule = ProductsInBasketCondition.objects.create(quantity=2) rule.products.add(product) rule.save() campaign = BasketCampaign.objects.create(active=True, shop=shop, name="test", public_name="test") campaign.conditions.add(rule) DiscountFromCategoryProducts.objects.create( campaign=campaign, discount_amount=discount_amount_value, category=category) assert rule.matches(basket, []) basket.uncache() # the discount amount should not exceed the minimum price. as the configued discount # will exceed, it should limit the discount amount final_lines = basket.get_final_lines() expected_discount_amount = basket.create_price( (single_product_price - single_product_min_price) * quantity) original_price = basket.create_price(single_product_price) * quantity line = final_lines[0] assert line.discount_amount == expected_discount_amount assert basket.total_price == original_price - expected_discount_amount
def test_variation_templates(browser, admin_user, live_server, settings): cache.clear() # Avoid cache from past tests shop = get_default_shop() configuration_key = "saved_variation_templates" assert configuration.get(shop, configuration_key, None) is None product = create_product("test_sku", shop, default_price=10, mode=ProductMode.VARIABLE_VARIATION_PARENT) assert product.is_variation_parent() initialize_admin_browser_test(browser, live_server, settings) browser.driver.set_window_size(800, 1000) url = reverse("shuup_admin:shop_product.edit_variation", kwargs={"pk": product.pk}) browser.visit("%s%s" % (live_server, url + "#variables-section")) click_element(browser, '#variables-section > div:nth-child(1) > a:nth-child(2)') wait_until_condition(browser, lambda x: x.is_text_present("New template")) assert len( ProductVariationVariable.objects.filter(product=product)) == 0 # Size assert len(ProductVariationVariableValue.objects.all() ) == 0 # Assert no variations are active click_element(browser, '.fa.fa-plus') wait_until_condition(browser, lambda x: x.is_text_present("New template")) browser.fill("variables-template_name", printable_gibberish()) # variables-template_name click_element(browser, '#save_template_name') wait_until_condition(browser, lambda x: not x.is_text_present("New template")) assert len(configuration.get(shop, configuration_key, [])) == 1 click_element(browser, '#variables-section > div:nth-child(1) > a:nth-child(2)') click_element(browser, "#variation-variable-editor") browser.find_by_xpath( '//*[@id="variation-variable-editor"]/div/div/select/option[2]' ).first.click() wait_until_condition(browser, lambda x: x.is_text_present("Add new variable")) click_element(browser, ".btn.btn-lg.btn-text") browser.find_by_xpath( '//*[@id="product-variable-wrap"]/div/div[2]/div[1]/table/tbody[1]/tr/td[1]/input' ).first.fill("Size") click_element(browser, ".btn.btn-xs.btn-text") browser.find_by_xpath( '//*[@id="product-variable-wrap"]/div/div[2]/div[1]/table/tbody[2]/tr/td[1]/input' ).first.fill("S") click_element(browser, ".fa.fa-check-circle") # Save assert len( ProductVariationVariable.objects.filter(product=product)) == 1 # Size assert len(ProductVariationVariableValue.objects.all()) == 1 # S click_element(browser, '#variables-section > div:nth-child(1) > a:nth-child(2)') click_element(browser, "#variation-variable-editor") # id_variables-data cache.clear() # Avoid cache from past tests assert len(configuration.get(shop, configuration_key, [])) == 1 browser.find_by_xpath( '//*[@id="variation-variable-editor"]/div/div/select/option[2]' ).first.click() template_data = configuration.get(shop, configuration_key, [])[0].get('data')[0] browser_data = json.loads(browser.find_by_css( "#id_variables-data").value).get('variable_values')[0] assert browser_data == template_data # assert shown template data matches template data in the db
def test_discount_no_limits(rf, include_tax): # check whether is it possible to earn money buying from us # adding lots of effects request, shop, _ = initialize_test(rf, include_tax) basket = get_basket(request) supplier = get_default_supplier(shop) single_product_price = Decimal(50) quantity = 4 discount_amount = (single_product_price * quantity * 2) discount_percentage = Decimal(1.9) # 190% second_product = create_product(printable_gibberish(), shop=shop, supplier=supplier, default_price=single_product_price) # the expected discount amount should not be greater than the products expected_discount_amount = basket.create_price( single_product_price) * quantity category = CategoryFactory() # create basket rule that requires 2 products in basket product = create_product(printable_gibberish(), shop=shop, supplier=supplier, default_price=single_product_price) ShopProduct.objects.get(shop=shop, product=product).categories.add(category) basket.add_product(supplier=supplier, shop=shop, product=product, quantity=quantity) basket.shipping_method = get_shipping_method(shop=shop) basket.save() rule = ProductsInBasketCondition.objects.create(quantity=2) rule.products.add(product) rule.save() # BasketCampaign campaign = BasketCampaign.objects.create(active=True, shop=shop, name="test", public_name="test") campaign.conditions.add(rule) # effect 1 - categories from products DiscountFromCategoryProducts.objects.create( campaign=campaign, discount_percentage=discount_percentage, category=category) # effect 2 - discount from products effect2 = DiscountFromProduct.objects.create( campaign=campaign, discount_amount=discount_amount) effect2.products.add(product) # effect 3 - basket discount BasketDiscountAmount.objects.create(campaign=campaign, discount_amount=discount_amount) # effect 4 - basket discount percetage BasketDiscountPercentage.objects.create( campaign=campaign, discount_percentage=discount_percentage) basket.uncache() final_lines = basket.get_final_lines() assert len(final_lines) == 3 original_price = basket.create_price(single_product_price) * quantity line = final_lines[0] assert line.discount_amount == expected_discount_amount assert basket.total_price == original_price - expected_discount_amount # effect free - aaaww yes, it's free effect_free = FreeProductLine.objects.create(campaign=campaign, quantity=1) effect_free.products.add(second_product) basket.uncache() final_lines = basket.get_final_lines() assert len(final_lines) == 4 line = final_lines[0] assert line.discount_amount == expected_discount_amount assert basket.total_price == original_price - expected_discount_amount # CatalogCampaign catalog_campaign = CatalogCampaign.objects.create(active=True, shop=shop, name="test2", public_name="test") product_filter = ProductFilter.objects.create() product_filter.products.add(product) catalog_campaign.filters.add(product_filter) # effect 5 - ProductDiscountAmount ProductDiscountAmount.objects.create(campaign=catalog_campaign, discount_amount=discount_amount) # effct 6 - ProductDiscountPercentage ProductDiscountPercentage.objects.create( campaign=catalog_campaign, discount_percentage=discount_percentage) basket.uncache() final_lines = basket.get_final_lines() assert len(final_lines) == 4 line = final_lines[0] assert line.discount_amount == expected_discount_amount assert basket.total_price == original_price - expected_discount_amount
def test_multiple_campaigns_cheapest_price(): rf = RequestFactory() request, shop, group = initialize_test(rf, False) price = shop.create_price product_price = "100" discount_percentage = "0.30" discount_amount_value = "10" total_discount_amount = "50" expected_total = price(product_price) - (Decimal(discount_percentage) * price(product_price)) matching_expected_total = price(product_price) - price( total_discount_amount) category = get_default_category() supplier = get_default_supplier() product = create_product(printable_gibberish(), shop=shop, supplier=supplier, default_price=product_price) shop_product = product.get_shop_instance(shop) shop_product.categories.add(category) # create catalog campaign catalog_filter = ProductFilter.objects.create() catalog_filter.products.add(product) catalog_campaign = CatalogCampaign.objects.create(shop=shop, active=True, name="test") catalog_campaign.filters.add(catalog_filter) cdp = ProductDiscountPercentage.objects.create( campaign=catalog_campaign, discount_percentage=discount_percentage) # create basket campaign condition = CategoryProductsBasketCondition.objects.create( operator=ComparisonOperator.EQUALS, quantity=1) condition.categories.add(category) basket_campaign = BasketCampaign.objects.create(shop=shop, public_name="test", name="test", active=True) basket_campaign.conditions.add(condition) effect = DiscountFromProduct.objects.create( campaign=basket_campaign, discount_amount=discount_amount_value) effect.products.add(product) # add product to basket basket = get_basket(request) basket.add_product(supplier=supplier, shop=shop, product=product, quantity=1) basket.shipping_method = get_shipping_method(shop=shop) final_lines = basket.get_final_lines() assert len(final_lines) == 2 assert basket.total_price == expected_total effect.discount_amount = total_discount_amount effect.save() basket.uncache() catalog_campaign.save() # save to bump caches basket_campaign.save() # save to bump caches assert basket.total_price == matching_expected_total # discount is now bigger than the original effect.delete() # remove effect basket.uncache() catalog_campaign.save() # save to bump caches basket_campaign.save() # save to bump caches assert BasketLineEffect.objects.count() == 0 assert basket.total_price == expected_total # add new effect effect = DiscountFromCategoryProducts.objects.create( category=category, campaign=basket_campaign, discount_amount=discount_amount_value) assert basket.total_price == expected_total effect.discount_amount = total_discount_amount effect.save() basket.uncache() catalog_campaign.save() # save to bump caches basket_campaign.save() # save to bump caches assert basket.total_price == matching_expected_total # discount is now bigger than the original
def test_variation_detail_view(client): shop = get_default_shop() parent = create_product(printable_gibberish(), shop) assert parent.mode == ProductMode.NORMAL volume = ProductVariationVariable.objects.create( identifier="volume", name="volume", product=parent, ordering=0, ) for index, value in enumerate(["1ml", "2ml", "3ml"]): ProductVariationVariableValue.objects.create( identifier=value, value=value, variable=volume, ordering=index, ) color = ProductVariationVariable.objects.create( identifier="color", name="color", product=parent, ordering=1, ) for index, value in enumerate(["red", "green", "blue"]): ProductVariationVariableValue.objects.create( identifier=value, value=value, variable=color, ordering=index, ) child = create_product(printable_gibberish(), shop) values = [volume.values.all()[1], color.values.all()[1]] assert child.link_to_parent( parent, variables={ volume: values[0], color: values[1], }, ) assert parent.mode == ProductMode.VARIABLE_VARIATION_PARENT variation_url = "{}?variation={}".format( reverse("shuup:product", kwargs=dict(pk=parent.pk, slug=parent.slug)), child.sku ) response = client.get(variation_url + "BAD_VARIATION_SKU") assert response.status_code == 404 response = client.get(variation_url) assert response.status_code == 200 assert response.context_data["shop_product"] assert response.context_data["selected_variation"] == child assert response.context_data["selected_variation_values"] == [v.pk for v in values]
def test_create_order(admin_user, currency): create_default_order_statuses() shop = get_default_shop() shop.currency = currency tax = get_default_tax() Currency.objects.get_or_create(code=currency, decimal_places=2) shop.save() sm = get_default_shipping_method() pm = get_default_payment_method() contact = create_random_person(locale="en_US", minimum_name_comp_len=5) product = create_product(sku=printable_gibberish(), supplier=get_default_supplier(), shop=shop) assert not Order.objects.count() client = _get_client(admin_user) lines = [ { "type": "product", "product": product.id, "quantity": "1", "base_unit_price_value": "5.00" }, { "type": "product", "product": product.id, "quantity": "2", "base_unit_price_value": "1.00", "discount_amount_value": "0.50" }, { "type": "other", "sku": "hello", "text": "A greeting", "quantity": 1, "base_unit_price_value": "3.5" }, { "type": "text", "text": "This was an order!", "quantity": 0 }, ] response = client.post("/api/shuup/order/", content_type="application/json", data=json.dumps({ "shop": shop.pk, "shipping_method": sm.pk, "payment_method": pm.pk, "customer": contact.pk, "lines": lines })) assert response.status_code == 201 assert Order.objects.count() == 1 order = Order.objects.first() assert order.shop == shop assert order.shipping_method == sm assert order.payment_method == pm assert order.customer == contact assert order.creator == admin_user assert order.billing_address == contact.default_billing_address.to_immutable( ) assert order.shipping_address == contact.default_shipping_address.to_immutable( ) assert order.payment_status == PaymentStatus.NOT_PAID assert order.shipping_status == ShippingStatus.NOT_SHIPPED assert order.status == OrderStatus.objects.get_default_initial() assert order.taxful_total_price_value == decimal.Decimal(10) assert order.lines.count( ) == 6 # shipping line, payment line, 2 product lines, 2 other lines assert order.currency == currency for idx, line in enumerate(order.lines.all()[:4]): assert line.quantity == decimal.Decimal(lines[idx].get("quantity")) assert line.base_unit_price_value == decimal.Decimal(lines[idx].get( "base_unit_price_value", 0)) assert line.discount_amount_value == decimal.Decimal(lines[idx].get( "discount_amount_value", 0)) # Test tax summary response_data = json.loads(response.content.decode("utf-8")) # Tax summary should not be present here assert "summary" not in response_data response = client.get('/api/shuup/order/{}/taxes/'.format(order.pk)) assert response.status_code == status.HTTP_200_OK response_data = json.loads(response.content.decode("utf-8")) assert "lines" in response_data assert "summary" in response_data line_summary = response_data["lines"] summary = response_data["summary"] first_tax_summary = summary[0] assert int(first_tax_summary["tax_id"]) == tax.id assert first_tax_summary["tax_rate"] == tax.rate first_line_summary = line_summary[0] assert "tax" in first_line_summary
def test_product_visibility(rf, admin_user, regular_user): anon_contact = get_person_contact(AnonymousUser()) shop_product = get_default_shop_product() admin_contact = get_person_contact(admin_user) regular_contact = get_person_contact(regular_user) configuration.set(None, get_all_seeing_key(admin_contact), True) with modify( shop_product.product, deleted=True ): # NB: assigning to `product` here works because `get_shop_instance` populates `_product_cache` assert error_exists( shop_product.get_visibility_errors(customer=anon_contact), "product_deleted") assert error_exists( shop_product.get_visibility_errors(customer=admin_contact), "product_deleted") with pytest.raises(ProductNotVisibleProblem): shop_product.raise_if_not_visible(anon_contact) assert not shop_product.is_list_visible() with modify(shop_product, visibility_limit=ProductVisibility.VISIBLE_TO_ALL, visibility=ShopProductVisibility.NOT_VISIBLE): assert error_exists( shop_product.get_visibility_errors(customer=anon_contact), "product_not_visible") assert error_does_not_exist( shop_product.get_visibility_errors(customer=admin_contact), "product_not_visible") assert not shop_product.is_list_visible() with modify(shop_product, visibility_limit=ProductVisibility.VISIBLE_TO_LOGGED_IN, visibility=ShopProductVisibility.ALWAYS_VISIBLE): assert error_exists( shop_product.get_visibility_errors(customer=anon_contact), "product_not_visible_to_anonymous") assert error_does_not_exist( shop_product.get_visibility_errors(customer=admin_contact), "product_not_visible_to_anonymous") customer_group = get_default_customer_group() grouped_user = get_user_model().objects.create_user( username=printable_gibberish(20)) grouped_contact = get_person_contact(grouped_user) with modify(shop_product, visibility_limit=ProductVisibility.VISIBLE_TO_GROUPS, visibility=ShopProductVisibility.ALWAYS_VISIBLE): shop_product.visibility_groups.add(customer_group) customer_group.members.add(grouped_contact) customer_group.members.remove(get_person_contact(regular_user)) assert error_does_not_exist( shop_product.get_visibility_errors(customer=grouped_contact), "product_not_visible_to_group") assert error_does_not_exist( shop_product.get_visibility_errors(customer=admin_contact), "product_not_visible_to_group") assert error_exists( shop_product.get_visibility_errors(customer=regular_contact), "product_not_visible_to_group") configuration.set(None, get_all_seeing_key(admin_contact), False)
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))) 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()