Exemplo n.º 1
0
def get_package_product():
    """
    :rtype: shuup.core.models.Product
    """
    shop = get_default_shop()
    supplier = get_default_supplier()
    return create_package_product("PackageParent", shop=shop, supplier=supplier)
Exemplo n.º 2
0
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
Exemplo n.º 3
0
def get_package_product():
    """
    :rtype: shuup.core.models.Product
    """
    shop = get_default_shop()
    supplier = get_default_supplier()
    return create_package_product("PackageParent", shop=shop, supplier=supplier)
Exemplo n.º 4
0
def test_order_creator_with_package_product(rf, admin_user):
    if "shuup.simple_supplier" not in settings.INSTALLED_APPS:
        pytest.skip("Need shuup.simple_supplier in INSTALLED_APPS")
    from shuup_tests.simple_supplier.utils import get_simple_supplier

    shop = get_default_shop()
    supplier = get_simple_supplier()
    package_product = create_package_product("Package-Product-Test", shop=shop, supplier=supplier,
                                             children=2)
    shop_product = package_product.get_shop_instance(shop)
    quantity_map = package_product.get_package_child_to_quantity_map()
    product_1, product_2 = quantity_map.keys()
    product_1.stock_behavior = StockBehavior.STOCKED
    product_1.save()
    product_2.stock_behavior = StockBehavior.STOCKED
    product_2.save()

    assert quantity_map[product_1] == 1
    assert quantity_map[product_2] == 2

    supplier.adjust_stock(product_1.pk, 1)
    supplier.adjust_stock(product_2.pk, 2)

    assert supplier.get_stock_status(product_1.pk).logical_count == 1
    assert supplier.get_stock_status(product_2.pk).logical_count == 2

    creator = OrderCreator()

    # There should be no exception when creating order with only package product
    source = seed_source(admin_user)
    source.add_line(
        type=OrderLineType.PRODUCT,
        product=package_product,
        supplier=supplier,
        quantity=1,
        base_unit_price=source.create_price(10),
    )
    order = creator.create_order(source)

    # However, there should not be enough stock for both package and child products
    source = seed_source(admin_user)
    source.add_line(
        type=OrderLineType.PRODUCT,
        product=package_product,
        supplier=supplier,
        quantity=1,
        base_unit_price=source.create_price(10),
    )
    source.add_line(
        type=OrderLineType.PRODUCT,
        product=product_1,
        supplier=supplier,
        quantity=1,
        base_unit_price=source.create_price(10),
    )

    # And a validation error should be raised
    with pytest.raises(ValidationError):
        order = creator.create_order(source)
Exemplo n.º 5
0
def test_order_creator_with_package_product(rf, admin_user):
    if "shuup.simple_supplier" not in settings.INSTALLED_APPS:
        pytest.skip("Need shuup.simple_supplier in INSTALLED_APPS")
    from shuup_tests.simple_supplier.utils import get_simple_supplier

    shop = get_default_shop()
    supplier = get_simple_supplier()
    package_product = create_package_product("Package-Product-Test", shop=shop, supplier=supplier,
                                             children=2)
    shop_product =  package_product.get_shop_instance(shop)
    quantity_map = package_product.get_package_child_to_quantity_map()
    product_1, product_2 = quantity_map.keys()
    product_1.stock_behavior = StockBehavior.STOCKED
    product_1.save()
    product_2.stock_behavior = StockBehavior.STOCKED
    product_2.save()

    assert quantity_map[product_1] == 1
    assert quantity_map[product_2] == 2

    supplier.adjust_stock(product_1.pk, 1)
    supplier.adjust_stock(product_2.pk, 2)

    assert supplier.get_stock_status(product_1.pk).logical_count == 1
    assert supplier.get_stock_status(product_2.pk).logical_count == 2

    creator = OrderCreator()

    # There should be no exception when creating order with only package product
    source = seed_source(admin_user)
    source.add_line(
        type=OrderLineType.PRODUCT,
        product=package_product,
        supplier=supplier,
        quantity=1,
        base_unit_price=source.create_price(10),
    )
    order = creator.create_order(source)

    # However, there should not be enough stock for both package and child products
    source = seed_source(admin_user)
    source.add_line(
        type=OrderLineType.PRODUCT,
        product=package_product,
        supplier=supplier,
        quantity=1,
        base_unit_price=source.create_price(10),
    )
    source.add_line(
        type=OrderLineType.PRODUCT,
        product=product_1,
        supplier=supplier,
        quantity=1,
        base_unit_price=source.create_price(10),
    )

    # And a validation error should be raised
    with pytest.raises(ValidationError):
        order = creator.create_order(source)
Exemplo n.º 6
0
def test_product_package(admin_user):
    shop1 = get_default_shop()
    supplier1 = create_simple_supplier("supplier1")
    product4 = create_package_product("product4", shop=shop1, supplier=supplier1)

    request = get_request("/api/shuup/front/shop_products/", admin_user)
    response = FrontShopProductViewSet.as_view({"get": "list"})(request)
    response.render()
    products_data = json.loads(response.content.decode("utf-8"))
    products = sorted(products_data, key=lambda p: p["id"])
    assert products[0]["product_id"] == product4.id
    assert products[0]["is_orderable"] is True
    assert len(products[0]["package_content"]) == product4.get_all_package_children().count()
Exemplo n.º 7
0
def test_modify_order_with_package_product(admin_user):
    package_children = 4
    package = create_package_product("parent", get_default_shop(), get_default_supplier(), 100, package_children)
    order, source = get_order_and_source(admin_user, package)
    source.add_line(
        type=OrderLineType.OTHER,
        quantity=1,
        base_unit_price=source.create_price(10),
        require_verification=True,
    )
    modifier = OrderModifier()
    modifier.update_order_from_source(source, order)
    assert order.lines.products().count() == 1 + package_children  # parent + children
    assert order.lines.other().count() == 2  # one added here + one from original order
Exemplo n.º 8
0
def test_modify_order_with_package_product(admin_user):
    package_children = 4
    package = create_package_product("parent", get_default_shop(), get_default_supplier(), 100, package_children)
    order, source = get_order_and_source(admin_user, package)
    source.add_line(
        type=OrderLineType.OTHER,
        quantity=1,
        base_unit_price=source.create_price(10),
        require_verification=True,
    )
    modifier = OrderModifier()
    modifier.update_order_from_source(source, order)
    assert order.lines.products().count() == 1 + package_children  # parent + children
    assert order.lines.other().count() == 2  # one added here + one from original order
Exemplo n.º 9
0
def test_basket_whit_package_products(rf):
    with override_settings(**CORE_BASKET_SETTINGS):
        shop = factories.get_default_shop()
        user = factories.create_random_user()
        supplier = factories.get_default_supplier()
        request = apply_request_middleware(rf.get("/"), user=user, shop=shop)
        basket_class = cached_load("SHUUP_BASKET_CLASS_SPEC")
        basket = basket_class(request, "basket", shop=shop)
        assert basket.shop == shop
        supplier.stock_managed = True
        supplier.save()

        package_product = factories.create_package_product("product",
                                                           shop,
                                                           supplier,
                                                           default_price=10,
                                                           children=4)
        product_stock = supplier.get_stock_status(package_product.id)
        stock = product_stock.physical_count
        supplier.adjust_stock(package_product.id, -(stock - 6))
        product_stock = supplier.get_stock_status(package_product.id)

        for index, child_product in enumerate(
                list(package_product.get_package_child_to_quantity_map().keys(
                )), 1):
            child_product_stock = supplier.get_stock_status(child_product.id)
            stock = child_product_stock.physical_count
            supplier.adjust_stock(child_product.id, -(stock - 10 * index))
            child_product_stock = supplier.get_stock_status(child_product.id)

        basket.add_product(supplier,
                           shop,
                           package_product,
                           2,
                           force_new_line=True)
        basket.add_product(supplier,
                           shop,
                           package_product,
                           2,
                           force_new_line=True)
        basket.add_product(supplier,
                           shop,
                           package_product,
                           2,
                           force_new_line=True)

        basket.uncache()
        assert len(basket.get_unorderable_lines()) == 0
Exemplo n.º 10
0
def get_unstocked_package_product_and_stocked_child(shop, supplier, child_logical_quantity=1):
    package_product = create_package_product("Package-Product-Test", shop=shop, supplier=supplier, children=1)

    quantity_map = package_product.get_package_child_to_quantity_map()
    assert len(quantity_map.keys()) == 1

    child_product = list(quantity_map.keys())[0]

    assert quantity_map[child_product] == 1

    supplier.adjust_stock(child_product.id, child_logical_quantity)

    stock_status = supplier.get_stock_status(child_product.id)
    assert stock_status.logical_count == child_logical_quantity

    return package_product, child_product
Exemplo n.º 11
0
def get_unstocked_package_product_and_stocked_child(shop, supplier, child_logical_quantity=1):
    package_product = create_package_product("Package-Product-Test", shop=shop, supplier=supplier, children=1)

    quantity_map = package_product.get_package_child_to_quantity_map()
    assert len(quantity_map.keys()) == 1

    child_product = list(quantity_map.keys())[0]

    assert quantity_map[child_product] == 1

    supplier.adjust_stock(child_product.id, child_logical_quantity)

    stock_status = supplier.get_stock_status(child_product.id)
    assert stock_status.logical_count == child_logical_quantity

    return package_product, child_product
Exemplo n.º 12
0
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
Exemplo n.º 13
0
def test_get_products(admin_user):
    shop1 = get_default_shop()

    person1 = create_random_person()
    person1.user = admin_user
    person1.save()

    person2 = create_random_person()
    person2.save()

    client = _get_client(admin_user)
    client.customer = person1
    client.shop = shop1

    # list all orderable products
    request = get_request("/api/shuup/front/products/", admin_user, shop1, person1)
    response = FrontProductViewSet.as_view({"get": "list"})(request)
    response.render()
    assert response.status_code == status.HTTP_200_OK
    products_data = json.loads(response.content.decode("utf-8"))
    assert len(products_data) == 0

    shop2 = Shop.objects.create()
    supplier1 = create_simple_supplier("supplier1")
    supplier2 = create_simple_supplier("supplier2")

    # create 2 products for shop2
    product1 = create_product("product1", shop=shop2, supplier=supplier1)
    product2 = create_product("product2", shop=shop2, supplier=supplier2)

    # add images for products 1 and 2
    add_product_image(product1)
    add_product_image(product2)

    # list all orderable products - None, since the 2 just created are for shop2
    request = get_request("/api/shuup/front/products/", admin_user, shop1, person1)
    response = FrontProductViewSet.as_view({"get": "list"})(request)
    response.render()
    products_data = json.loads(response.content.decode("utf-8"))
    assert len(products_data) == 0

    # create the product for shop1 but set it as not visible
    product3 = create_product("product3", shop=shop1, supplier=supplier1)
    shop1_product3 = ShopProduct.objects.get(shop=shop1, product=product3)
    shop1_product3.visibility = ShopProductVisibility.NOT_VISIBLE
    shop1_product3.save()

    # list all orderable products - No one yet
    request = get_request("/api/shuup/front/products/", admin_user, shop1, person1)
    response = FrontProductViewSet.as_view({"get": "list"})(request)
    response.render()
    products_data = json.loads(response.content.decode("utf-8"))
    assert len(products_data) == 0

    # now product3 becomes visible
    shop1_product3.visibility = ShopProductVisibility.ALWAYS_VISIBLE
    shop1_product3.save()
    request = get_request("/api/shuup/front/products/", admin_user, shop1, person1)
    response = FrontProductViewSet.as_view({"get": "list"})(request)
    response.render()
    products_data = json.loads(response.content.decode("utf-8"))
    assert len(products_data) == 1
    assert products_data[0]["shop_products"][0]["orderable"] is True

    # product should be visible only for some groups
    group = create_random_contact_group()
    group.members.add(person2)
    product3.visibility = ProductVisibility.VISIBLE_TO_GROUPS
    product3.save()
    shop1_product3.visibility_limit = ProductVisibility.VISIBLE_TO_GROUPS
    shop1_product3.visibility_groups.add(group)
    shop1_product3.save()

    request = get_request("/api/shuup/front/products/", admin_user, shop1, person1)
    response = FrontProductViewSet.as_view({"get": "list"})(request)
    response.render()
    products_data = json.loads(response.content.decode("utf-8"))
    assert len(products_data) == 0

    # visible for person2 which is in the same group
    request = get_request("/api/shuup/front/products/", admin_user, shop1, person2)
    response = FrontProductViewSet.as_view({"get": "list"})(request)
    response.render()
    products_data = json.loads(response.content.decode("utf-8"))
    assert len(products_data) == 1

    # product1 and product2 are visible in shop2
    request = get_request("/api/shuup/front/products/", admin_user, shop2, person1)
    response = FrontProductViewSet.as_view({"get": "list"})(request)
    response.render()
    products_data = json.loads(response.content.decode("utf-8"))
    products = sorted(products_data, key=lambda p: p["id"])
    assert products[0]["id"] == product1.id
    assert products[0]["shop_products"][0]["orderable"] is True
    assert products[1]["id"] == product2.id
    assert products[1]["shop_products"][0]["orderable"] is True

    # check for medias
    assert products[0]["primary_image"]["file"] == request.build_absolute_uri(product1.primary_image.url)
    assert products[0]["media"][0]["file"] == request.build_absolute_uri(product1.media.first().url)
    assert products[0]["media"][1]["file"] == request.build_absolute_uri(product1.media.all()[1].url)

    assert products[1]["primary_image"]["file"] == request.build_absolute_uri(product2.primary_image.url)
    assert products[1]["media"][0]["file"] == request.build_absolute_uri(product2.media.first().url)
    assert products[1]["media"][1]["file"] == request.build_absolute_uri(product2.media.all()[1].url)

    # ordering by -id
    request = get_request("/api/shuup/front/products/?ordering=-id", admin_user, shop2, person1)
    response = FrontProductViewSet.as_view({"get": "list"})(request)
    response.render()
    products_data = json.loads(response.content.decode("utf-8"))
    assert products_data[0]["id"] == product2.id
    assert products_data[0]["shop_products"][0]["orderable"] is True
    assert products_data[1]["id"] == product1.id
    assert products_data[1]["shop_products"][0]["orderable"] is True

    # add categories to products
    category1 = Category.objects.create(parent=None, identifier="category1", name="category1")
    shop2_product1 = ShopProduct.objects.get(shop=shop2, product=product1)
    shop2_product1.primary_category = category1
    shop2_product1.categories.add(category1)
    shop2_product1.save()
    category2 = Category.objects.create(parent=None, identifier="category2", name="category2")
    shop2_product2 = ShopProduct.objects.get(shop=shop2, product=product2)
    shop2_product2.primary_category = category2
    shop2_product2.categories.add(category2)
    shop2_product2.save()

    # fetch by category1
    request = get_request("/api/shuup/front/products/?category=%d" % category1.id, admin_user, shop2, person1)
    response = FrontProductViewSet.as_view({"get": "list"})(request)
    response.render()
    products_data = json.loads(response.content.decode("utf-8"))
    assert len(products_data) == 1
    assert products_data[0]["id"] == product1.id

    # fetch by category2
    request = get_request("/api/shuup/front/products/?category=%d" % category2.id, admin_user, shop2, person1)
    response = FrontProductViewSet.as_view({"get": "list"})(request)
    response.render()
    products_data = json.loads(response.content.decode("utf-8"))
    assert len(products_data) == 1
    assert products_data[0]["id"] == product2.id

    # create a package product
    product4 = create_package_product("product4", shop=shop1, supplier=supplier1)

    # make product3 orderable again
    product3.visibility = ProductVisibility.VISIBLE_TO_ALL
    product3.save()
    shop1_product3.visibility_limit = ProductVisibility.VISIBLE_TO_ALL
    shop1_product3.visibility_groups.all().delete()
    shop1_product3.save()

    # product3 and product4 are visible in shop1
    request = get_request("/api/shuup/front/products/", admin_user, shop1, person1)
    response = FrontProductViewSet.as_view({"get": "list"})(request)
    response.render()
    products_data = json.loads(response.content.decode("utf-8"))
    products = sorted(products_data, key=lambda p: p["id"])
    assert products[0]["id"] == product3.id
    assert products[0]["shop_products"][0]["orderable"] is True
    assert products[1]["id"] == product4.id
    assert products[1]["shop_products"][0]["orderable"] is True

    # change the shop of the first child product to make the package not orderable
    sp = product4.get_all_package_children()[0].shop_products.first()
    sp.shop = shop2
    sp.save()

    # product3 is orderable and product4 doesn't
    request = get_request("/api/shuup/front/products/", admin_user, shop1, person1)
    response = FrontProductViewSet.as_view({"get": "list"})(request)
    response.render()
    products_data = json.loads(response.content.decode("utf-8"))
    products = sorted(products_data, key=lambda p: p["id"])
    assert products[0]["id"] == product3.id
    assert products[0]["shop_products"][0]["orderable"] is True
    assert products[1]["id"] == product4.id
    assert products[1]["shop_products"][0]["orderable"] is False

    # assign cross sell of product1 and product2
    ProductCrossSell.objects.create(product1=product1, product2=product2, type=ProductCrossSellType.RECOMMENDED)
    ProductCrossSell.objects.create(product1=product1, product2=product2, type=ProductCrossSellType.RELATED)
    ProductCrossSell.objects.create(product1=product1, product2=product2, type=ProductCrossSellType.COMPUTED)
    ProductCrossSell.objects.create(product1=product1, product2=product2, type=ProductCrossSellType.BOUGHT_WITH)

    # product1 must have cross shell of product2
    request = get_request("/api/shuup/front/products/", admin_user, shop2, person1)
    response = FrontProductViewSet.as_view({"get": "list"})(request)
    response.render()
    products_data = json.loads(response.content.decode("utf-8"))
    products = sorted(products_data, key=lambda p: p["id"])

    assert products[0]["id"] == product1.id
    assert products[0]["cross_sell"]["recommended"] == [product2.id]
    assert products[0]["cross_sell"]["related"] == [product2.id]
    assert products[0]["cross_sell"]["computed"] == [product2.id]
    assert products[0]["cross_sell"]["bought_with"] == [product2.id]

    assert products[1]["id"] == product2.id
    assert products[1]["cross_sell"]["recommended"] == []
    assert products[1]["cross_sell"]["related"] == []
    assert products[1]["cross_sell"]["computed"] == []
    assert products[1]["cross_sell"]["bought_with"] == []
Exemplo n.º 14
0
def test_get_products(admin_user):
    shop1 = get_default_shop()

    person1 = create_random_person()
    person1.user = admin_user
    person1.save()

    person2 = create_random_person()
    person2.save()

    client = _get_client(admin_user)
    client.customer = person1
    client.shop = shop1

    # list all orderable products
    request = get_request("/api/shuup/front/products/", admin_user, shop1,
                          person1)
    response = FrontProductViewSet.as_view({"get": "list"})(request)
    response.render()
    assert response.status_code == status.HTTP_200_OK
    products_data = json.loads(response.content.decode("utf-8"))
    assert len(products_data) == 0

    shop2 = Shop.objects.create()
    supplier1 = create_simple_supplier("supplier1")
    supplier2 = create_simple_supplier("supplier2")

    # create 2 products for shop2
    product1 = create_product("product1", shop=shop2, supplier=supplier1)
    product2 = create_product("product2", shop=shop2, supplier=supplier2)

    # add images for products 1 and 2
    add_product_image(product1)
    add_product_image(product2)

    # list all orderable products - None, since the 2 just created are for shop2
    request = get_request("/api/shuup/front/products/", admin_user, shop1,
                          person1)
    response = FrontProductViewSet.as_view({"get": "list"})(request)
    response.render()
    products_data = json.loads(response.content.decode("utf-8"))
    assert len(products_data) == 0

    # create the product for shop1 but set it as not visible
    product3 = create_product("product3", shop=shop1, supplier=supplier1)
    shop1_product3 = ShopProduct.objects.get(shop=shop1, product=product3)
    shop1_product3.visibility = ShopProductVisibility.NOT_VISIBLE
    shop1_product3.save()

    # list all orderable products - No one yet
    request = get_request("/api/shuup/front/products/", admin_user, shop1,
                          person1)
    response = FrontProductViewSet.as_view({"get": "list"})(request)
    response.render()
    products_data = json.loads(response.content.decode("utf-8"))
    assert len(products_data) == 0

    # now product3 becomes visible
    shop1_product3.visibility = ShopProductVisibility.ALWAYS_VISIBLE
    shop1_product3.save()
    request = get_request("/api/shuup/front/products/", admin_user, shop1,
                          person1)
    response = FrontProductViewSet.as_view({"get": "list"})(request)
    response.render()
    products_data = json.loads(response.content.decode("utf-8"))
    assert len(products_data) == 1
    assert products_data[0]["shop_products"][0]["orderable"] is True

    # product should be visible only for some groups
    group = create_random_contact_group()
    group.members.add(person2)
    product3.visibility = ProductVisibility.VISIBLE_TO_GROUPS
    product3.save()
    shop1_product3.visibility_limit = ProductVisibility.VISIBLE_TO_GROUPS
    shop1_product3.visibility_groups.add(group)
    shop1_product3.save()

    request = get_request("/api/shuup/front/products/", admin_user, shop1,
                          person1)
    response = FrontProductViewSet.as_view({"get": "list"})(request)
    response.render()
    products_data = json.loads(response.content.decode("utf-8"))
    assert len(products_data) == 0

    # visible for person2 which is in the same group
    request = get_request("/api/shuup/front/products/", admin_user, shop1,
                          person2)
    response = FrontProductViewSet.as_view({"get": "list"})(request)
    response.render()
    products_data = json.loads(response.content.decode("utf-8"))
    assert len(products_data) == 1

    # product1 and product2 are visible in shop2
    request = get_request("/api/shuup/front/products/", admin_user, shop2,
                          person1)
    response = FrontProductViewSet.as_view({"get": "list"})(request)
    response.render()
    products_data = json.loads(response.content.decode("utf-8"))
    products = sorted(products_data, key=lambda p: p["id"])
    assert products[0]["id"] == product1.id
    assert products[0]["shop_products"][0]["orderable"] is True
    assert products[1]["id"] == product2.id
    assert products[1]["shop_products"][0]["orderable"] is True

    # check for medias
    assert products[0]["primary_image"]["file"] == request.build_absolute_uri(
        product1.primary_image.url)
    assert products[0]["media"][0]["file"] == request.build_absolute_uri(
        product1.media.first().url)
    assert products[0]["media"][1]["file"] == request.build_absolute_uri(
        product1.media.all()[1].url)

    assert products[1]["primary_image"]["file"] == request.build_absolute_uri(
        product2.primary_image.url)
    assert products[1]["media"][0]["file"] == request.build_absolute_uri(
        product2.media.first().url)
    assert products[1]["media"][1]["file"] == request.build_absolute_uri(
        product2.media.all()[1].url)

    # ordering by -id
    request = get_request("/api/shuup/front/products/?ordering=-id",
                          admin_user, shop2, person1)
    response = FrontProductViewSet.as_view({"get": "list"})(request)
    response.render()
    products_data = json.loads(response.content.decode("utf-8"))
    assert products_data[0]["id"] == product2.id
    assert products_data[0]["shop_products"][0]["orderable"] is True
    assert products_data[1]["id"] == product1.id
    assert products_data[1]["shop_products"][0]["orderable"] is True

    # add categories to products
    category1 = Category.objects.create(parent=None,
                                        identifier="category1",
                                        name="category1")
    shop2_product1 = ShopProduct.objects.get(shop=shop2, product=product1)
    shop2_product1.primary_category = category1
    shop2_product1.categories.add(category1)
    shop2_product1.save()
    category2 = Category.objects.create(parent=None,
                                        identifier="category2",
                                        name="category2")
    shop2_product2 = ShopProduct.objects.get(shop=shop2, product=product2)
    shop2_product2.primary_category = category2
    shop2_product2.categories.add(category2)
    shop2_product2.save()

    # fetch by category1
    request = get_request(
        "/api/shuup/front/products/?category=%d" % category1.id, admin_user,
        shop2, person1)
    response = FrontProductViewSet.as_view({"get": "list"})(request)
    response.render()
    products_data = json.loads(response.content.decode("utf-8"))
    assert len(products_data) == 1
    assert products_data[0]["id"] == product1.id

    # fetch by category2
    request = get_request(
        "/api/shuup/front/products/?category=%d" % category2.id, admin_user,
        shop2, person1)
    response = FrontProductViewSet.as_view({"get": "list"})(request)
    response.render()
    products_data = json.loads(response.content.decode("utf-8"))
    assert len(products_data) == 1
    assert products_data[0]["id"] == product2.id

    # create a package product
    product4 = create_package_product("product4",
                                      shop=shop1,
                                      supplier=supplier1)

    # make product3 orderable again
    product3.visibility = ProductVisibility.VISIBLE_TO_ALL
    product3.save()
    shop1_product3.visibility_limit = ProductVisibility.VISIBLE_TO_ALL
    shop1_product3.visibility_groups.all().delete()
    shop1_product3.save()

    # product3 and product4 are visible in shop1
    request = get_request("/api/shuup/front/products/", admin_user, shop1,
                          person1)
    response = FrontProductViewSet.as_view({"get": "list"})(request)
    response.render()
    products_data = json.loads(response.content.decode("utf-8"))
    products = sorted(products_data, key=lambda p: p["id"])
    assert products[0]["id"] == product3.id
    assert products[0]["shop_products"][0]["orderable"] is True
    assert products[1]["id"] == product4.id
    assert products[1]["shop_products"][0]["orderable"] is True

    # change the shop of the first child product to make the package not orderable
    sp = product4.get_all_package_children()[0].shop_products.first()
    sp.shop = shop2
    sp.save()

    # product3 is orderable and product4 doesn't
    request = get_request("/api/shuup/front/products/", admin_user, shop1,
                          person1)
    response = FrontProductViewSet.as_view({"get": "list"})(request)
    response.render()
    products_data = json.loads(response.content.decode("utf-8"))
    products = sorted(products_data, key=lambda p: p["id"])
    assert products[0]["id"] == product3.id
    assert products[0]["shop_products"][0]["orderable"] is True
    assert products[1]["id"] == product4.id
    assert products[1]["shop_products"][0]["orderable"] is False

    # assign cross sell of product1 and product2
    ProductCrossSell.objects.create(product1=product1,
                                    product2=product2,
                                    type=ProductCrossSellType.RECOMMENDED)
    ProductCrossSell.objects.create(product1=product1,
                                    product2=product2,
                                    type=ProductCrossSellType.RELATED)
    ProductCrossSell.objects.create(product1=product1,
                                    product2=product2,
                                    type=ProductCrossSellType.COMPUTED)
    ProductCrossSell.objects.create(product1=product1,
                                    product2=product2,
                                    type=ProductCrossSellType.BOUGHT_WITH)

    # product1 must have cross shell of product2
    request = get_request("/api/shuup/front/products/", admin_user, shop2,
                          person1)
    response = FrontProductViewSet.as_view({"get": "list"})(request)
    response.render()
    products_data = json.loads(response.content.decode("utf-8"))
    products = sorted(products_data, key=lambda p: p["id"])

    assert products[0]["id"] == product1.id
    assert products[0]["cross_sell"]["recommended"] == [product2.id]
    assert products[0]["cross_sell"]["related"] == [product2.id]
    assert products[0]["cross_sell"]["computed"] == [product2.id]
    assert products[0]["cross_sell"]["bought_with"] == [product2.id]

    assert products[1]["id"] == product2.id
    assert products[1]["cross_sell"]["recommended"] == []
    assert products[1]["cross_sell"]["related"] == []
    assert products[1]["cross_sell"]["computed"] == []
    assert products[1]["cross_sell"]["bought_with"] == []