def test_matching_range_different_suppliers(admin_user): supplier_1 = get_supplier("simple_supplier", name="Supplier 1", shop=get_shop()) supplier_2 = get_supplier("simple_supplier", name="Supplier 2", shop=get_shop()) # this service will only be available for supplier_2 shipping_method = get_default_shipping_method() shipping_method.supplier = supplier_2 shipping_method.save() ranges_data = [ ("10", "20", decimal.Decimal("20"), "Mid range"), ] _assign_component_for_service(shipping_method, ranges_data) # as the shipping method is set for supplier_2, it shouldn't raise for items of supplier_1 source = _get_source_for_weight( admin_user, shipping_method, "shipping_method", decimal.Decimal("3"), "sup1", supplier=supplier_1 ) assert not list(shipping_method.get_unavailability_reasons(source)) assert not list(shipping_method.get_costs(source)) # raise when correct supplier is set source = _get_source_for_weight( admin_user, shipping_method, "shipping_method", decimal.Decimal("3"), "sup2", supplier=supplier_2 ) assert list(shipping_method.get_unavailability_reasons(source)) assert not list(shipping_method.get_costs(source)) # don't raise with correct supplier and correct weight range source = _get_source_for_weight( admin_user, shipping_method, "shipping_method", decimal.Decimal("14"), "sup3", supplier=supplier_2 ) assert not list(shipping_method.get_unavailability_reasons(source)) assert list(shipping_method.get_costs(source))
def test_package_edit_view(admin_user, rf, supplier_enabled): shop = get_default_shop() supplier = get_supplier(SimpleSupplierModule.identifier, shop=shop, stock_managed=True) supplier.enabled = supplier_enabled supplier.save() package = create_package_product(printable_gibberish(), shop, supplier) request = apply_all_middleware(rf.get("/"), user=admin_user) response = ProductPackageView.as_view()(request=request, pk=package.pk) product_ids = [] for shop_product in ShopProduct.objects.filter( suppliers=supplier, product__mode=ProductMode.NORMAL): supplier.adjust_stock(product_id=shop_product.product_id, delta=shop_product.product_id) product_ids.append(shop_product.product_id) assert response.status_code == 200 response.render() content = response.content.decode("utf-8") for product_id in product_ids: is_inside = ("Logical count: %s" % product_id) in content assert is_inside == supplier_enabled
def test_different_suppliers(admin_user): supplier_1 = get_supplier("simple_supplier", name="Supplier 1", shop=get_shop()) supplier_2 = get_supplier("simple_supplier", name="Supplier 2", shop=get_shop()) # this service will only be available for supplier_2 shipping_method = get_default_shipping_method() shipping_method.supplier = supplier_2 shipping_method.save() component = WaivingCostBehaviorComponent.objects.create( price_value=decimal.Decimal(10), waive_limit_value=decimal.Decimal(20), ) shipping_method.behavior_components.add(component) source = seed_source(admin_user) product_1 = create_product(sku="sup1", shop=source.shop, supplier=supplier_1, default_price=25) product_2 = create_product(sku="sup2", shop=source.shop, supplier=supplier_2, default_price=4) source.add_line( type=OrderLineType.PRODUCT, product=product_1, supplier=supplier_1, base_unit_price=source.create_price(25), ) source.add_line( type=OrderLineType.PRODUCT, product=product_2, supplier=supplier_2, base_unit_price=source.create_price(4), ) source.shipping_method = shipping_method # there not waiving costs as the total is below 20 for supplier_2 costs = list(shipping_method.get_costs(source)) assert costs[0].price.value == decimal.Decimal(10) # add more items of supplier_2 source.add_line( type=OrderLineType.PRODUCT, product=product_2, supplier=supplier_2, quantity=10, base_unit_price=source.create_price(4), ) # now that the total is greater than 20, there is no cost costs = list(shipping_method.get_costs(source)) assert costs[0].price.value == decimal.Decimal(0)
def _add_products_to_basket(basket, product_count): supplier = factories.get_supplier("simple_supplier", basket.shop) for x in range(0, product_count): product = factories.create_product( sku="%s-%s" % (printable_gibberish(), x), shop=basket.shop, supplier=supplier, default_price=50 ) basket.add_product(supplier=supplier, shop=basket.shop, product=product, quantity=1) return basket
def test_order_shipments(rf, admin_user): shop = get_default_shop() supplier = get_supplier(module_identifier="simple_supplier", identifier="1", name="supplier") supplier.shops.add(shop) product = create_product("sku1", shop=shop, default_price=10) shop_product = product.get_shop_instance(shop=shop) shop_product.suppliers.set([supplier]) product_quantities = { supplier.pk: {product.pk: 20}, } def get_quantity(supplier, product): return product_quantities[supplier.pk][product.pk] order = create_empty_order(shop=shop) order.full_clean() order.save() order.change_status(OrderStatus.objects.get_default_processing()) # Let's test the order order status history section for superuser request = apply_request_middleware(rf.get("/"), user=admin_user, shop=shop) # Add product to order for supplier add_product_to_order(order, supplier, product, get_quantity(supplier, product), 8) context = OrderHistorySection.get_context_data(order, request) assert len(context) == 2 # Let's create staff user without any permissions staff_user = create_random_user(is_staff=True) group = get_default_permission_group() staff_user.groups.add(group) shop.staff_members.add(staff_user) request = apply_request_middleware(rf.get("/"), user=staff_user, shop=shop) context = OrderHistorySection.get_context_data(order, request) assert len(context) == 2 # works fine while rendering rendered_content = loader.render_to_string( OrderHistorySection.template, context={ OrderHistorySection.identifier: context, "order_status_history": OrderStatusHistory.objects.filter(order=order).order_by("-created_on"), }, ) assert force_text(OrderStatus.objects.get_default_initial().name) in rendered_content assert force_text(OrderStatus.objects.get_default_processing().name) in rendered_content client = Client() client.force_login(admin_user) # We should see the order status section assert OrderHistorySection.visible_for_object(order, request)
def test_delete_recover_product_variation(admin_user): """ Test that we can delete a variaiton and the recover it """ shop = factories.get_default_shop() supplier = factories.get_supplier("simple_supplier", shop) product = factories.create_product("parent-sku", shop=shop, supplier=supplier) shop_product = product.get_shop_instance(shop) view_url = reverse( "shuup_admin:shuup_product_variations.product.combinations", kwargs=dict(pk=shop_product.pk)) client = Client() client.force_login(admin_user) assert product.variation_children.count() == 0 create_payload = [{ "combination": { "Color": "Red", "Size": "L" }, "sku": "red-l", }, { "combination": { "Color": "Red", "Size": "XL" }, "sku": "red-xl", }] response = client.post(view_url, data=create_payload, content_type="application/json") assert response.status_code == 200 assert product.variation_children.count() == 2 assert Product.objects.count() == 3 delete_payload = [{"combination": {"Color": "Red", "Size": "L"}}] response = client.delete(view_url, data=delete_payload, content_type="application/json") assert response.status_code == 200 assert product.variation_children.count() == 2 # products are still linked assert Product.objects.filter(deleted=True).count() == 1 assert Product.objects.count() == 3 # create the variation again response = client.post(view_url, data=create_payload, content_type="application/json") assert response.status_code == 200 assert product.variation_children.count() == 2 assert Product.objects.filter(deleted=True).count() == 0 assert Product.objects.count() == 3
def test_package_edit_view(admin_user, rf, supplier_enabled): shop = get_default_shop() supplier = get_supplier(SimpleSupplierModule.identifier, shop=shop, stock_managed=True) supplier.enabled = supplier_enabled supplier.save() package = create_package_product(printable_gibberish(), shop, supplier) request = apply_all_middleware(rf.get("/"), user=admin_user) response = ProductPackageView.as_view()(request=request, pk=package.pk) product_ids = [] for shop_product in ShopProduct.objects.filter(suppliers=supplier, product__mode=ProductMode.NORMAL): supplier.adjust_stock(product_id=shop_product.product_id, delta=shop_product.product_id) product_ids.append(shop_product.product_id) assert response.status_code == 200 response.render() content = response.content.decode("utf-8") for product_id in product_ids: is_inside = ("Logical count: %s" % product_id) in content assert is_inside == supplier_enabled
def test_resolve_product_url(): shop = factories.get_default_shop() product = factories.create_product("product", shop, factories.get_default_supplier(), "10") from shuup.front.template_helpers.urls import model_url product_url = reverse("shuup:product", kwargs=dict(pk=product.pk, slug=product.slug)) assert model_url({}, product) == product_url # create a new supplier and use it # the URL should still point to the default product URL (no supplier specific) # because the given supplier doesn't supplies the product supplier2 = factories.get_supplier("", shop) assert model_url({}, product, supplier=supplier2) == product_url shop_product = product.get_shop_instance(shop) shop_product.suppliers.add(supplier2) # now the url is supplier2 specific product_supplier2_url = reverse("shuup:supplier-product", kwargs=dict(pk=product.pk, slug=product.slug, supplier_pk=supplier2.pk)) assert model_url({}, product, supplier=supplier2) == product_supplier2_url
def test_delete_product_variation(admin_user): shop = factories.get_default_shop() supplier = factories.get_supplier("simple_supplier", shop) product = factories.create_product("parent-sku", shop=shop, supplier=supplier) shop_product = product.get_shop_instance(shop) view_url = reverse( "shuup_admin:shuup_product_variations.product.combinations", kwargs=dict(pk=shop_product.pk)) client = Client() client.force_login(admin_user) assert product.variation_children.count() == 0 create_payload = [ { "combination": { "Color": "Red", "Size": "L" }, "sku": "red-l", }, { "combination": { "Color": "Blue", "Size": "S" }, "sku": "blue-s", }, { "combination": { "Color": "Green", "Size": "XL" }, "sku": "green-xl", }, { "combination": { "Color": "Red", "Size": "XL" }, "sku": "red-xl", }, ] response = client.post( view_url, data=create_payload, content_type="application/json", ) assert response.status_code == 200 assert product.variation_children.count() == 4 assert Product.objects.filter(deleted=True).count() == 0 delete_payload = [ # can delete using the combination { "combination": { "Color": "Red", "Size": "L" } }, # or by sku { "sku": "red-xl" } ] response = client.delete( view_url, data=delete_payload, content_type="application/json", ) assert response.status_code == 200 assert product.variation_children.count() == 4 assert Product.objects.filter(deleted=True).count() == 2
def test_basket_with_package_product(admin_user): shop = factories.get_default_shop() factories.get_default_shipping_method() factories.get_default_payment_method() OrderStatusManager().ensure_default_statuses() client = get_client(admin_user) response = client.post("/api/shuup/basket/new/", format="json", data={"shop": shop.pk}) assert response.status_code == status.HTTP_201_CREATED basket_uuid = response.data["uuid"] supplier = factories.get_supplier(SimpleSupplierModule.identifier, shop=shop, stock_managed=True) # base product - 1kg of sand base_sand_product = factories.create_product("Sand", shop=shop, supplier=supplier, default_price="15.2", net_weight=Decimal(1)) # 10kg bag of sand - package made by 10kg of sand sand_bag_10kg_product = factories.create_product("Sand-bag-10-kg", shop=shop, supplier=supplier, default_price="149.9", net_weight=Decimal(10000)) sand_bag_10kg_product.make_package({base_sand_product: 10}) sand_bag_10kg_product.save() # 18.45kg bag of sand - package made by 18.45kg of sand sand_bag_1845kg_product = factories.create_product( "Sand-bag-1845-kg", shop=shop, supplier=supplier, default_price="179.9", net_weight=Decimal(18450)) sand_bag_1845kg_product.make_package({base_sand_product: 18.45}) sand_bag_1845kg_product.save() # 25kg bag of sand - package made by 25kg of sand sand_bag_25kg_product = factories.create_product("Sand-bag-25-kg", shop=shop, supplier=supplier, default_price="2450.25", net_weight=Decimal(25000)) sand_bag_25kg_product.make_package({base_sand_product: 25}) sand_bag_25kg_product.save() initial_stock = 55 # put 55 sands (55kg) in stock supplier.adjust_stock(base_sand_product.id, initial_stock) stock_status = supplier.get_stock_status(base_sand_product.id) assert stock_status.physical_count == initial_stock assert stock_status.logical_count == initial_stock # zero stock for packages assert supplier.get_stock_status( sand_bag_10kg_product.id).logical_count == 0 assert supplier.get_stock_status( sand_bag_1845kg_product.id).logical_count == 0 assert supplier.get_stock_status( sand_bag_25kg_product.id).logical_count == 0 # add all the 3 packages to the basket, this will require (10 + 18.45 + 25 = 53.45 Sands) for product in [ sand_bag_10kg_product, sand_bag_1845kg_product, sand_bag_25kg_product ]: response = client.post( "/api/shuup/basket/{}/add/".format(basket_uuid), format="json", data={ "shop": shop.pk, "product": product.id }, ) assert response.status_code == status.HTTP_200_OK # get the basket response = client.get("/api/shuup/basket/{}/".format(basket_uuid)) assert response.status_code == status.HTTP_200_OK assert response.data["validation_errors"] == [] # now add more 25kg and it shouldn't have enough stock response = client.post( "/api/shuup/basket/{}/add/".format(basket_uuid), format="json", data={ "shop": shop.pk, "product": sand_bag_25kg_product.id }, ) assert response.status_code == status.HTTP_400_BAD_REQUEST assert "Insufficient quantity" in response.data["error"] # create order anyway response = client.post( "/api/shuup/basket/{}/create_order/".format(basket_uuid), format="json") assert response.status_code == status.HTTP_201_CREATED order = Order.objects.get(id=response.data["id"]) line_counter = Counter() for line in order.lines.products(): line_counter[line.product.id] += line.quantity assert bankers_round(line_counter[base_sand_product.id]) == bankers_round( Decimal(10) + Decimal(18.45) + Decimal(25)) assert line_counter[sand_bag_10kg_product.id] == 1 assert line_counter[sand_bag_1845kg_product.id] == 1 assert line_counter[sand_bag_25kg_product.id] == 1
def test_category_detail_filters(client, reindex_catalog): shop = factories.get_default_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}) category = factories.get_default_category() # Important! Activate supplier filter. set_configuration( shop=shop, data={ "filter_products_by_supplier": True, "filter_products_by_supplier_ordering": 1, }, ) product_data = [("laptop", 1500), ("keyboard", 150), ("mouse", 150)] products = [] for sku, price_value in product_data: products.append( factories.create_product(sku, shop=shop, default_price=price_value)) supplier_data = [ ("Johnny Inc", 0.5), ("Mike Inc", 0.9), ("Simon Inc", 0.8), ] for name, percentage_from_original_price in supplier_data: supplier = factories.get_supplier("simple_supplier", shop, name=name) for product in products: shop_product = product.get_shop_instance(shop) shop_product.suppliers.add(supplier) shop_product.primary_category = category shop_product.categories.add(category) shop_product.save() supplier_price = ( percentage_from_original_price * [price for sku, price in product_data if product.sku == sku][0]) SupplierPrice.objects.create(supplier=supplier, shop=shop, product=product, amount_value=supplier_price) strategy = "shuup.testing.supplier_pricing.supplier_strategy:CheapestSupplierPriceSupplierStrategy" with override_settings(SHUUP_PRICING_MODULE="supplier_pricing", SHUUP_SHOP_PRODUCT_SUPPLIERS_STRATEGY=strategy): with override_current_theme_class( ClassicGrayTheme, shop): # Ensure settings is refreshed from DB reindex_catalog() laptop = [ product for product in products if product.sku == "laptop" ][0] keyboard = [ product for product in products if product.sku == "keyboard" ][0] mouse = [ product for product in products if product.sku == "mouse" ][0] # Let's get products for Johnny supplier_johnny = Supplier.objects.filter( name="Johnny Inc").first() soup = _get_category_detail_soup(client, category, supplier_johnny.pk) laptop_product_box = soup.find("div", {"id": "product-%s" % laptop.pk}) _assert_supplier_info(laptop_product_box, "Johnny Inc") _assert_product_price(laptop_product_box, 750) # Now here when the category view is filtered based on supplier # the product urls should lead to supplier product url so we # can show details and prices for correct supplier. _assert_product_url(laptop_product_box, supplier_johnny, laptop) # Let's test rest of the products and suppliers keyboard_product_box = soup.find( "div", {"id": "product-%s" % keyboard.pk}) _assert_supplier_info(keyboard_product_box, "Johnny Inc") _assert_product_price(keyboard_product_box, 75) _assert_product_url(keyboard_product_box, supplier_johnny, keyboard) mike_supplier = Supplier.objects.filter(name="Mike Inc").first() soup = _get_category_detail_soup(client, category, mike_supplier.pk) keyboard_product_box = soup.find( "div", {"id": "product-%s" % keyboard.pk}) _assert_supplier_info(keyboard_product_box, "Mike Inc") _assert_product_price(keyboard_product_box, 135) _assert_product_url(keyboard_product_box, mike_supplier, keyboard) simon_supplier = Supplier.objects.filter(name="Simon Inc").first() soup = _get_category_detail_soup(client, category, simon_supplier.pk) mouse_product_box = soup.find("div", {"id": "product-%s" % mouse.pk}) _assert_supplier_info(mouse_product_box, "Simon Inc") _assert_product_price(mouse_product_box, 120) _assert_product_url(mouse_product_box, simon_supplier, mouse)
def test_category_detail_multiselect_supplier_filters(client, reindex_catalog): shop = factories.get_default_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}) category = factories.get_default_category() # Important! Activate supplier filter. set_configuration( shop=shop, data={ "filter_products_by_supplier": True, "filter_products_by_supplier_ordering": 1, "filter_products_by_supplier_multiselect_enabled": True, }, ) supplier_data = [ ("Johnny Inc", 0.5), ("Mike Inc", 0.9), ("Simon Inc", 0.8), ] for name, percentage_from_original_price in supplier_data: supplier = factories.get_supplier("simple_supplier", shop, name=name) sku = name price_value = 10 product = factories.create_product(sku, shop=shop, default_price=price_value) shop_product = product.get_shop_instance(shop) shop_product.suppliers.add(supplier) shop_product.primary_category = category shop_product.categories.add(category) shop_product.save() supplier_price = percentage_from_original_price * price_value SupplierPrice.objects.create(supplier=supplier, shop=shop, product=product, amount_value=supplier_price) reindex_catalog() strategy = "shuup.testing.supplier_pricing.supplier_strategy:CheapestSupplierPriceSupplierStrategy" with override_settings(SHUUP_PRICING_MODULE="supplier_pricing", SHUUP_SHOP_PRODUCT_SUPPLIERS_STRATEGY=strategy): with override_current_theme_class( ClassicGrayTheme, shop): # Ensure settings is refreshed from DB johnny_supplier = Supplier.objects.filter( name="Johnny Inc").first() mike_supplier = Supplier.objects.filter(name="Mike Inc").first() simon_supplier = Supplier.objects.filter(name="Simon Inc").first() soup = _get_category_detail_soup_multiselect( client, category, [johnny_supplier.pk]) assert len(soup.findAll("div", {"class": "single-product"})) == 1 soup = _get_category_detail_soup_multiselect( client, category, [johnny_supplier.pk, mike_supplier.pk]) assert len(soup.findAll("div", {"class": "single-product"})) == 2 soup = _get_category_detail_soup_multiselect( client, category, [johnny_supplier.pk, mike_supplier.pk, simon_supplier.pk]) assert len(soup.findAll("div", {"class": "single-product"})) == 3
def test_order_refunds_with_multiple_suppliers(): shop = get_default_shop() supplier1 = get_supplier("simple_supplier", shop=shop, identifier="1", name="supplier1") supplier2 = get_supplier("simple_supplier", shop=shop, identifier="2", name="supplier2") supplier3 = get_supplier("simple_supplier", shop=shop, identifier="3", name="s") 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_update_product_variation(admin_user): shop = factories.get_default_shop() supplier = factories.get_supplier("simple_supplier", shop) product = factories.create_product("parent-sku", shop=shop, supplier=supplier) shop_product = product.get_shop_instance(shop) view_url = reverse( "shuup_admin:shuup_product_variations.product.combinations", kwargs=dict(pk=shop_product.pk)) client = Client() client.force_login(admin_user) assert product.variation_children.count() == 0 create_payload = [{ "combination": { "Color": "Red", "Size": "L" }, "sku": "red-l", "price": "15.5", "stock_count": 20, }] response = client.post( view_url, data=create_payload, content_type="application/json", ) assert response.status_code == 200 update_payload = [{ "combination": { "Color": "Red", "Size": "L" }, "sku": "red-l2", "price": "21", "stock_count": 4, }] response = client.post( view_url, data=update_payload, content_type="application/json", ) assert response.status_code == 200 all_combinations = list(product.get_all_available_combinations()) red_l_combination = [ comb for comb in all_combinations if comb["sku_part"] == 'red-l' ][0] red_l_shop_product = ShopProduct.objects.get( product_id=red_l_combination["result_product_pk"]) assert red_l_shop_product.product.sku == "red-l2" assert red_l_shop_product.default_price_value == Decimal("21") assert supplier.get_stock_status( red_l_combination["result_product_pk"]).logical_count == 4 # test partial update, just price partial_update_payload = [{ "combination": { "Color": "Red", "Size": "L" }, "sku": "red-l2", "price": "30", }] response = client.post( view_url, data=partial_update_payload, content_type="application/json", ) assert response.status_code == 200 red_l_shop_product.refresh_from_db() assert red_l_shop_product.default_price_value == Decimal("30") # the product id must be returned into the response response_data = response.json() assert response_data["combinations"][0]["product_id"] assert response_data["combinations"][0]["sku"] == "red-l2"
def test_create_product_variation(admin_user): shop = factories.get_default_shop() supplier = factories.get_supplier("simple_supplier", shop) product = factories.create_product("parent-sku", shop=shop, supplier=supplier) shop_product = product.get_shop_instance(shop) client = Client() client.force_login(admin_user) assert product.variation_children.count() == 0 payload = [ { "combination": { "Color": "Red", "Size": "XL" }, "sku": "red-xl", "price": "3.5", "stock_count": 15, }, { "combination": { "Color": "Red", "Size": "L" }, "sku": "red-l", "price": "15.5", "stock_count": 20, }, { "combination": { "Color": "Blue", "Size": "S" }, "sku": "blue-s", "price": "16", "stock_count": 2, }, ] url = reverse( "shuup_admin:shuup_product_variations.product.combinations", kwargs=dict(pk=shop_product.pk) ) response = client.post( url, data=payload, content_type="application/json", ) assert response.status_code == 200 assert product.variation_children.count() == 3 response = client.get( reverse("shuup_admin:shuup_product_variations.product.variations", kwargs={"pk": product.pk}), content_type="application/json", ) assert response.status_code == 200 data = json.loads(response.content.decode("utf-8")) assert len(data["variables"]) == 2 assert len(data["values"]) == 2 # One set of values to each variable color = ProductVariationVariable.objects.filter(identifier="color").first() assert color url_for_color = reverse( "shuup_admin:shuup_product_variations.product.variations_variable", kwargs={"pk": color.pk} ) assert color.name == "Color" with switch_language(color, 'fi'): assert color.name == "Color" response = client.post( url_for_color, data={"language_code": "fi", "name": "Väri"}, content_type="application/json", ) assert response.status_code == 200 color.refresh_from_db() assert color.name == "Color" with switch_language(color, 'fi'): assert color.name == "Väri" assert color.ordering == 0 response = client.post( url_for_color, data={"ordering": 3}, content_type="application/json", ) color.refresh_from_db() assert color.ordering == 3 extralarge = ProductVariationVariableValue.objects.filter(identifier="xl").first() assert extralarge url_for_extralarge = reverse( "shuup_admin:shuup_product_variations.product.variations_variable_value", kwargs={"pk": extralarge.pk} ) assert extralarge.value == "XL" with switch_language(extralarge, "fi"): assert extralarge.value == "XL" response = client.post( url_for_extralarge, data={"language_code": "fi", "name": "Melko iso"}, content_type="application/json", ) assert response.status_code == 200 extralarge.refresh_from_db() assert extralarge.value == "XL" with switch_language(extralarge, "fi"): assert extralarge.value == "Melko iso" assert extralarge.ordering == 0 response = client.post( url_for_extralarge, data={"ordering": 3}, content_type="application/json", ) extralarge.refresh_from_db() assert extralarge.ordering == 3
def test_create_product_variation(admin_user): shop = factories.get_default_shop() supplier = factories.get_supplier("simple_supplier", shop) product = factories.create_product("parent-sku", shop=shop, supplier=supplier) shop_product = product.get_shop_instance(shop) client = Client() client.force_login(admin_user) assert product.variation_children.count() == 0 payload = [ { "combination": { "Color": "Red", "Size": "L" }, "sku": "red-l", "price": "15.5", "stock_count": 20, }, { "combination": { "Color": "Blue", "Size": "S" }, "sku": "blue-s", "price": "16", "stock_count": 2, }, ] url = reverse("shuup_admin:shuup_product_variations.product.combinations", kwargs=dict(pk=shop_product.pk)) response = client.get(url) assert response.status_code == 200 data = json.loads(response.content.decode("utf-8")) assert len(data["combinations"]) == 0 assert len(data["product_data"]) == 0 response = client.post( url, data=payload, content_type="application/json", ) assert response.status_code == 200 assert product.variation_children.count() == 2 all_combinations = list(product.get_all_available_combinations()) red_l_combination = [ comb for comb in all_combinations if comb["sku_part"] == 'red-l' ][0] blue_s_combination = [ comb for comb in all_combinations if comb["sku_part"] == 'blue-s' ][0] red_l_shop_product = ShopProduct.objects.get( product_id=red_l_combination["result_product_pk"]) blue_s_shop_product = ShopProduct.objects.get( product_id=blue_s_combination["result_product_pk"]) assert red_l_shop_product.product.sku == "red-l" assert blue_s_shop_product.product.sku == "blue-s" assert red_l_shop_product.default_price_value == Decimal("15.5") assert blue_s_shop_product.default_price_value == Decimal("16") assert supplier.get_stock_status( red_l_combination["result_product_pk"]).logical_count == 20 assert supplier.get_stock_status( blue_s_combination["result_product_pk"]).logical_count == 2 response = client.get(url) assert response.status_code == 200 data = json.loads(response.content.decode("utf-8")) assert len(data["combinations"]) == 2 assert len(data["product_data"]) == 2 shop_product.suppliers.clear() # Without supplier nothing is returned response = client.get(url) assert response.status_code == 200 data = json.loads(response.content.decode("utf-8")) assert len(data["combinations"]) == 0 assert len(data["product_data"]) == 0
def test_order_shipments(rf, admin_user): shop = get_default_shop() supplier1 = get_supplier(module_identifier="simple_supplier", identifier="1", name="supplier1") supplier1.shops.add(shop) supplier2 = get_supplier(module_identifier="simple_supplier", identifier="2", name="supplier1") supplier2.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]) product2 = create_product("sku3", shop=shop, default_price=10, shipping_mode=ShippingMode.NOT_SHIPPED) shop_product2 = product1.get_shop_instance(shop=shop) shop_product2.suppliers.set([supplier2]) product_quantities = { supplier1.pk: { product1.pk: 20 }, supplier2.pk: { product2.pk: 10 } } def get_quantity(supplier, product): return product_quantities[supplier.pk][product.pk] order = create_empty_order(shop=shop) order.full_clean() order.save() # Let's test the order shipment section for superuser request = apply_request_middleware(rf.get("/"), user=admin_user, shop=shop) # Add product 3 to order for supplier 2 add_product_to_order(order, supplier2, product2, get_quantity(supplier2, product2), 8) # Product is not shippable so order section should not be available assert not ShipmentSection.visible_for_object(order, request) # Add product 2 to order for supplier 1 add_product_to_order(order, supplier1, product1, get_quantity(supplier1, product1), 7) # Now we should see the shipment section assert ShipmentSection.visible_for_object(order, request) # Make order fully paid so we can start creting shipments and refunds order.cache_prices() order.check_all_verified() order.create_payment(order.taxful_total_price) assert order.is_paid() product_summary = order.get_product_summary() assert product_summary[product1.pk]["unshipped"] == 20 assert product_summary[product2.pk]["unshipped"] == 0 assert product_summary[product2.pk]["ordered"] == 10 # Fully ship the order order.create_shipment({product1: 5}, supplier=supplier1) order.create_shipment({product1: 5}, supplier=supplier1) order.create_shipment({product1: 10}, supplier=supplier1) assert not order.get_unshipped_products() assert not order.is_fully_shipped() context = ShipmentSection.get_context_data(order, request) assert len(context["suppliers"]) == 2 assert len(context["create_urls"].keys()) == 2 # One for each supplier assert len(context["delete_urls"].keys()) == 3 # One for each shipment # Let's create staff user without any permissions staff_user = create_random_user(is_staff=True) group = get_default_permission_group() staff_user.groups.add(group) shop.staff_members.add(staff_user) request = apply_request_middleware(rf.get("/"), user=staff_user, shop=shop) context = ShipmentSection.get_context_data(order, request) assert len(context["suppliers"]) == 2 assert len(context["create_urls"].keys()) == 0 assert len(context["delete_urls"].keys()) == 0 assert len(context["set_sent_urls"].keys()) == 0 set_permissions_for_group(group, ["order.create-shipment"]) request = apply_request_middleware(rf.get("/"), user=staff_user, shop=shop) context = ShipmentSection.get_context_data(order, request) assert len(context["suppliers"]) == 2 assert len(context["create_urls"].keys()) == 2 assert len(context["delete_urls"].keys()) == 0 assert len(context["set_sent_urls"].keys()) == 0 set_permissions_for_group(group, [ "order.create-shipment", "order.delete-shipment", "order.set-shipment-sent" ]) request = apply_request_middleware(rf.get("/"), user=staff_user, shop=shop) context = ShipmentSection.get_context_data(order, request) assert len(context["suppliers"]) == 2 assert len(context["create_urls"].keys()) == 2 assert len(context["delete_urls"].keys()) == 3 assert len(context["set_sent_urls"].keys()) == 3 # works fine while rendering rendered_content = loader.render_to_string( ShipmentSection.template, context={ ShipmentSection.identifier: context, "order": order, }, ) all_urls = list(context["delete_urls"].values()) all_urls.extend(list(context["set_sent_urls"].values())) for url in all_urls: assert url in rendered_content assert order.get_sent_shipments().count() == 0 order.shipments.filter(status=ShipmentStatus.NOT_SENT) == 3 client = Client() client.force_login(admin_user) # mark all shipments as sent! for mark_sent_url in context["set_sent_urls"].values(): response = client.post(mark_sent_url) assert response.status_code == 302 order.refresh_from_db() assert order.is_fully_shipped() assert order.get_sent_shipments().count() == 3 order.shipments.filter(status=ShipmentStatus.NOT_SENT) == 0 # Make product1 unshipped product1.shipping_mode = ShippingMode.NOT_SHIPPED product1.save() # We still should see the order shipment section since existing shipments assert ShipmentSection.visible_for_object(order, request) # list all shipments in shipments list view response = client.get("{}?jq={}".format( reverse("shuup_admin:order.shipments.list"), json.dumps({ "perPage": 10, "page": 1 }))) assert response.status_code == 200 data = json.loads(response.content) assert len(data["items"]) == 3 for item in data["items"]: assert item["status"] == "Sent" # Let's delete all shipments since both products is unshipped and we # don't need those. for shipment in order.shipments.all(): shipment.soft_delete() assert not ShipmentSection.visible_for_object(order, request)
def test_get_best_selling_products_per_supplier(reindex_catalog): from shuup.front.template_helpers import general context = get_jinja_context() reindex_catalog() # No products sold assert len(list(general.get_best_selling_products(context, n_products=3))) == 0 shop = get_default_shop() supplier = get_supplier("simple_supplier", shop, name="Supplier 1") supplier2 = get_supplier("simple_supplier", shop, name="Supplier 2") product1 = create_product("product1", shop, supplier, 10) product2 = create_product("product2", shop, supplier2, 20) create_order_with_product(product1, supplier, quantity=1, taxless_base_unit_price=10, shop=shop) create_order_with_product(product2, supplier2, quantity=2, taxless_base_unit_price=20, shop=shop) reindex_catalog() # Two products sold, but only one supplier best_selling_products = list( general.get_best_selling_products(context, n_products=3, supplier=supplier)) assert len(best_selling_products) == 1 assert product1 in best_selling_products assert product2 not in best_selling_products # Two products sold, but only one supplier cache.clear() best_selling_products = list( general.get_best_selling_products(context, n_products=3, supplier=supplier2)) assert len(best_selling_products) == 1 assert product1 not in best_selling_products assert product2 in best_selling_products # Make product 1 also sold by supplier2 shop_product = product1.get_shop_instance(shop) shop_product.suppliers.add(supplier2) reindex_catalog() cache.clear() best_selling_products = list( general.get_best_selling_products(context, n_products=3, supplier=supplier2)) assert len(best_selling_products ) == 1 # Since there isn't any orders yet for supplier 2 assert product2 in best_selling_products # get for all suppliers cache.clear() best_selling_products = list( general.get_best_selling_products(context, n_products=3)) assert len(best_selling_products) == 2 assert product1 in best_selling_products assert product2 in best_selling_products
def test_error_handling(admin_user): shop = factories.get_default_shop() supplier = factories.get_supplier("simple_supplier", shop) product = factories.create_product("parent-sku", shop=shop, supplier=supplier) shop_product = product.get_shop_instance(shop) view_url = reverse( "shuup_admin:shuup_product_variations.product.combinations", kwargs=dict(pk=shop_product.pk)) client = Client() client.force_login(admin_user) # missing fields invalid_create_payload = [{ "combination": { "Color": "Red", "Size": "L" }, }] response = client.post(view_url, data=invalid_create_payload, content_type="application/json") assert response.status_code == 400 result = response.json() assert result["code"] == "validation-fail" assert result["error"]["combinations"][0]['sku'][ 0] == "This field is required." # can't create using existing SKU invalid_create_payload = [{ "combination": { "Color": "Red", "Size": "L" }, "sku": product.sku }] response = client.post(view_url, data=invalid_create_payload, content_type="application/json") assert response.status_code == 400 result = response.json() assert result["error"] == "The SKU 'parent-sku' is already being used." assert result["code"] == "sku-exists" # successfully create invalid_create_payload = [{ "combination": { "Color": "Red", "Size": "L" }, "sku": "random" }] response = client.post(view_url, data=invalid_create_payload, content_type="application/json") assert response.status_code == 200 # can't update using existing SKU invalid_create_payload = [{ "combination": { "Color": "Red", "Size": "L" }, "sku": product.sku }] response = client.post(view_url, data=invalid_create_payload, content_type="application/json") assert response.status_code == 400 result = response.json() assert result["error"] == "The SKU 'parent-sku' is already being used." assert result["code"] == "sku-exists"
def test_basket_with_package_product(admin_user): with override_settings(**REQUIRED_SETTINGS): shop = factories.get_default_shop() factories.get_default_shipping_method() factories.get_default_payment_method() OrderStatusManager().ensure_default_statuses() client = get_client(admin_user) response = client.post("/api/shuup/basket/new/", format="json", data={"shop": shop.pk}) assert response.status_code == status.HTTP_201_CREATED basket_uuid = response.data["uuid"] supplier = factories.get_supplier(SimpleSupplierModule.identifier, shop=shop, stock_managed=True) # base product - 1kg of sand base_sand_product = factories.create_product( "Sand", shop=shop, supplier=supplier, default_price="15.2", net_weight=Decimal(1) ) # 10kg bag of sand - package made by 10kg of sand sand_bag_10kg_product = factories.create_product( "Sand-bag-10-kg", shop=shop, supplier=supplier, default_price="149.9", net_weight=Decimal(10000) ) sand_bag_10kg_product.make_package({ base_sand_product: 10 }) sand_bag_10kg_product.save() # 18.45kg bag of sand - package made by 18.45kg of sand sand_bag_1845kg_product = factories.create_product( "Sand-bag-1845-kg", shop=shop, supplier=supplier, default_price="179.9", net_weight=Decimal(18450) ) sand_bag_1845kg_product.make_package({ base_sand_product: 18.45 }) sand_bag_1845kg_product.save() # 25kg bag of sand - package made by 25kg of sand sand_bag_25kg_product = factories.create_product( "Sand-bag-25-kg", shop=shop, supplier=supplier, default_price="2450.25", net_weight=Decimal(25000) ) sand_bag_25kg_product.make_package({ base_sand_product: 25 }) sand_bag_25kg_product.save() initial_stock = 55 # put 55 sands (55kg) in stock supplier.adjust_stock(base_sand_product.id, initial_stock) stock_status = supplier.get_stock_status(base_sand_product.id) assert stock_status.physical_count == initial_stock assert stock_status.logical_count == initial_stock # zero stock for packages assert supplier.get_stock_status(sand_bag_10kg_product.id).logical_count == 0 assert supplier.get_stock_status(sand_bag_1845kg_product.id).logical_count == 0 assert supplier.get_stock_status(sand_bag_25kg_product.id).logical_count == 0 # add all the 3 packages to the basket, this will require (10 + 18.45 + 25 = 53.45 Sands) for product in [sand_bag_10kg_product, sand_bag_1845kg_product, sand_bag_25kg_product]: response = client.post("/api/shuup/basket/{}/add/".format(basket_uuid), format="json", data={ "shop": shop.pk, "product": product.id }) assert response.status_code == status.HTTP_200_OK # get the basket response = client.get("/api/shuup/basket/{}/".format(basket_uuid)) assert response.status_code == status.HTTP_200_OK assert response.data["validation_errors"] == [] # now add more 25kg and it shouldn't have enough stock response = client.post("/api/shuup/basket/{}/add/".format(basket_uuid), format="json", data={ "shop": shop.pk, "product": sand_bag_25kg_product.id }) assert response.status_code == status.HTTP_400_BAD_REQUEST assert "Insufficient stock" in response.data["error"] # create order anyway response = client.post("/api/shuup/basket/{}/create_order/".format(basket_uuid), format="json") assert response.status_code == status.HTTP_201_CREATED order = Order.objects.get(id=response.data["id"]) line_counter = Counter() for line in order.lines.products(): line_counter[line.product.id] += line.quantity assert bankers_round(line_counter[base_sand_product.id]) == bankers_round( Decimal(10) + Decimal(18.45) + Decimal(25) ) assert line_counter[sand_bag_10kg_product.id] == 1 assert line_counter[sand_bag_1845kg_product.id] == 1 assert line_counter[sand_bag_25kg_product.id] == 1
def test_order_arbitrary_refunds_with_multiple_suppliers(): shop = get_default_shop() supplier1 = get_supplier("simple_supplier", identifier="1", name="supplier1", shop=shop) supplier2 = get_supplier("simple_supplier", identifier="2", name="supplier2", shop=shop) supplier3 = get_supplier("simple_supplier", identifier="3", name="supplier3", shop=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_reorder_view(): shop = factories.get_default_shop() factories.get_default_shipping_method() factories.get_default_payment_method() supplier1 = factories.get_supplier(SimpleSupplierModule.identifier, shop=shop) supplier2 = factories.get_supplier(SimpleSupplierModule.identifier, shop=shop) assert supplier1.pk != supplier2.pk product_supplier1 = factories.create_product( "product_supplier1", shop=shop, supplier=supplier1, default_price=10, shipping_mode=ShippingMode.NOT_SHIPPED) product_supplier2 = factories.create_product( "product_supplier2", shop=shop, supplier=supplier2, default_price=20, shipping_mode=ShippingMode.NOT_SHIPPED) user = factories.create_random_user("en") user.set_password("user") user.save() customer = factories.create_random_person("en") customer.user = user customer.save() order = factories.create_random_order( customer=customer, shop=shop, products=[product_supplier1, product_supplier2], completion_probability=0, random_products=False) suppliers = [line.supplier for line in order.lines.products()] assert supplier1 in suppliers assert supplier2 in suppliers client = Client() client.login(username=user.username, password="******") # list orders response = client.get(reverse("shuup:personal-orders")) assert response.status_code == 200 content = response.content.decode("utf-8") assert "<td>%d</td>" % order.id in content assert "<td>Received</td>" in content # go to order detail response = client.get(reverse("shuup:show-order", kwargs=dict(pk=order.pk))) assert response.status_code == 200 content = response.content.decode("utf-8") assert "Add all products to cart" in content reorder_url = reverse("shuup:reorder-order", kwargs=dict(pk=order.pk)) assert reorder_url in content # reorder products response = client.get(reorder_url) assert response.status_code == 302 assert response.url.endswith(reverse("shuup:basket")) # go to basket response = client.get(response.url) assert response.status_code == 200 content = response.content.decode("utf-8") # ensure the basket contain those products and suppliers basket_key = client.session["basket_basket_key"]["key"] from shuup.front.models import StoredBasket basket = StoredBasket.objects.get(key=basket_key) lines = basket.data["lines"] product_supplier = [(line["product_id"], line["supplier_id"]) for line in lines] assert (product_supplier1.pk, supplier1.pk) in product_supplier assert (product_supplier2.pk, supplier2.pk) in product_supplier assert product_supplier1.name in content assert product_supplier2.name in content assert "You are unable to proceed to checkout!" not in content
def test_category_detail(client, reindex_catalog): shop = factories.get_default_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}) # Activate supplier filters to prove they don't effect results # without actually filtering something. There is separate tests # to do the more closer tests. set_configuration( shop=shop, data={ "filter_products_by_supplier": True, "filter_products_by_supplier_ordering": 1, }, ) category = factories.get_default_category() product_data = [("laptop", 1500), ("keyboard", 150), ("mouse", 150)] products = [] for sku, price_value in product_data: products.append(factories.create_product(sku, shop=shop, default_price=price_value)) supplier_data = [ ("Johnny Inc", 0.5), ("Mike Inc", 0.9), ("Simon Inc", 0.8), ] for name, percentage_from_original_price in supplier_data: supplier = factories.get_supplier("simple_supplier", shop, name=name) for product in products: shop_product = product.get_shop_instance(shop) shop_product.suppliers.add(supplier) shop_product.primary_category = category shop_product.save() supplier_price = ( percentage_from_original_price * [price for sku, price in product_data if product.sku == sku][0] ) SupplierPrice.objects.create(supplier=supplier, shop=shop, product=product, amount_value=supplier_price) strategy = "shuup.testing.supplier_pricing.supplier_strategy:CheapestSupplierPriceSupplierStrategy" with override_settings(SHUUP_PRICING_MODULE="supplier_pricing", SHUUP_SHOP_PRODUCT_SUPPLIERS_STRATEGY=strategy): with override_current_theme_class(ClassicGrayTheme, shop): # Ensure settings is refreshed from DB reindex_catalog() soup = _get_category_detail_soup(client, category) # Johnny Inc has the best prices for everything laptop = [product for product in products if product.sku == "laptop"][0] laptop_product_box = soup.find("div", {"id": "product-%s" % laptop.pk}) _assert_supplier_info(laptop_product_box, "Johnny Inc") _assert_product_price(laptop_product_box, 750) keyboard = [product for product in products if product.sku == "keyboard"][0] keyboard_product_box = soup.find("div", {"id": "product-%s" % keyboard.pk}) _assert_supplier_info(keyboard_product_box, "Johnny Inc") _assert_product_price(keyboard_product_box, 75) mouse = [product for product in products if product.sku == "mouse"][0] mouse_product_box = soup.find("div", {"id": "product-%s" % mouse.pk}) _assert_supplier_info(mouse_product_box, "Johnny Inc") _assert_product_price(mouse_product_box, 75) # Ok competition has done it job and the other suppliers # has to start adjust their prices. # Let's say Mike has the cheapest laptop mike_supplier = Supplier.objects.get(name="Mike Inc") SupplierPrice.objects.filter(supplier=mike_supplier, shop=shop, product=laptop).update(amount_value=333) reindex_catalog() soup = _get_category_detail_soup(client, category) laptop_product_box = soup.find("div", {"id": "product-%s" % laptop.pk}) _assert_supplier_info(laptop_product_box, "Mike Inc") _assert_product_price(laptop_product_box, 333) # Just to make sure Simon takes over the mouse biz simon_supplier = Supplier.objects.get(name="Simon Inc") SupplierPrice.objects.filter(supplier=simon_supplier, shop=shop, product=mouse).update(amount_value=1) reindex_catalog() soup = _get_category_detail_soup(client, category) mouse_product_box = soup.find("div", {"id": "product-%s" % mouse.pk}) _assert_supplier_info(mouse_product_box, "Simon Inc") _assert_product_price(mouse_product_box, 1)