Ejemplo n.º 1
0
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))
Ejemplo 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
Ejemplo n.º 3
0
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)
Ejemplo n.º 4
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
Ejemplo n.º 5
0
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)
Ejemplo n.º 6
0
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
Ejemplo n.º 7
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
Ejemplo n.º 8
0
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
Ejemplo n.º 9
0
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
Ejemplo n.º 10
0
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
Ejemplo n.º 11
0
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)
Ejemplo n.º 12
0
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
Ejemplo n.º 13
0
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()
Ejemplo n.º 14
0
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"
Ejemplo n.º 15
0
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
Ejemplo n.º 16
0
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
Ejemplo n.º 17
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)
Ejemplo n.º 18
0
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
Ejemplo n.º 19
0
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"
Ejemplo n.º 20
0
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
Ejemplo n.º 21
0
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()
Ejemplo n.º 22
0
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
Ejemplo n.º 23
0
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)