Esempio n. 1
0
def test_shipment_with_unshippable_products():
    shop = get_default_shop()
    supplier = get_default_supplier()

    product = create_product(
        "unshippable",
        shop=shop,
        supplier=supplier,
        default_price=5.55)
    product.shipping_mode = ShippingMode.NOT_SHIPPED
    product.save()
    order = _get_order(shop, supplier, stocked=False)
    initial_product_line_count = order.lines.products().count()
    add_product_to_order(order, supplier, product, quantity=4, taxless_base_unit_price=3)
    order.cache_prices()
    order.check_all_verified()
    order.save()

    assert order.shipments.count() == 0
    assert order.can_create_shipment()
    assert not order.can_set_complete()
    order.create_shipment_of_all_products(supplier=supplier)
    assert (order.lines.products().count() == initial_product_line_count + 1)

    assert order.shipments.count() == 1
    assert ShipmentProduct.objects.filter(shipment__order_id=order.id).count() == initial_product_line_count
    assert order.shipping_status == ShippingStatus.FULLY_SHIPPED
    assert order.can_set_complete()
Esempio n. 2
0
def test_computing_simple_product_relations(rf):
    shop = get_default_shop()
    supplier = get_default_supplier()
    product = create_product("simple-test-product", shop)
    related_product = create_product("simple-related-product", shop)
    quantities = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
    for quantity in quantities:
        order = create_order_with_product(product,
                                          supplier,
                                          quantity=1,
                                          taxless_base_unit_price=6,
                                          shop=shop)
        add_product_to_order(order,
                             supplier,
                             related_product,
                             quantity=quantity,
                             taxless_base_unit_price=6)

    assert ProductCrossSell.objects.count() == 0
    add_bought_with_relations_for_product(product.pk)
    assert ProductCrossSell.objects.count() == 1
    cross_sell_product = ProductCrossSell.objects.filter(
        product1=product).first()
    assert cross_sell_product.product2 == related_product
    assert cross_sell_product.weight == sum(quantities)

    add_bought_with_relations_for_product(related_product.id)
    assert ProductCrossSell.objects.count() == 2
    cross_sell_product = ProductCrossSell.objects.filter(
        product1=related_product).first()
    assert cross_sell_product.product2 == product
    assert cross_sell_product.weight == len(quantities)
Esempio n. 3
0
def test_rounding_with_taxes(prices):
    shop = get_default_shop()
    supplier = get_default_supplier()

    order = create_empty_order(shop=shop)
    order.save()
    product = create_product("test_sku", shop=shop, supplier=supplier)
    tax_rate = Decimal("0.22222")
    for x, price in enumerate(prices):
        add_product_to_order(
            order,
            supplier,
            product,
            quantity=Decimal("2.22"),
            taxless_base_unit_price=Decimal(price),
            tax_rate=tax_rate,
        )
    order.cache_prices()
    for x, order_line in enumerate(order.lines.all().order_by("ordering")):
        # Check that total prices calculated from priceful parts still matches
        assert _get_taxless_price(order_line) == order_line.taxless_price
        assert _get_taxful_price(order_line) == order_line.taxful_price
        assert order_line.price == (
            order_line.base_unit_price * order_line.quantity -
            order_line.discount_amount)
Esempio n. 4
0
def test_order_with_only_unshippable_products():
    shop = get_default_shop()
    supplier = get_default_supplier()

    order = create_empty_order(shop=shop)
    order.full_clean()
    order.save()

    product = create_product("unshippable",
                             shop=shop,
                             supplier=supplier,
                             default_price=5.55)
    product.shipping_mode = ShippingMode.NOT_SHIPPED
    product.save()
    add_product_to_order(order,
                         supplier,
                         product,
                         quantity=4,
                         taxless_base_unit_price=3)
    order.cache_prices()
    order.check_all_verified()
    order.save()

    assert not order.can_create_shipment()
    assert order.can_set_complete()
def _init_test_with_variations():
    shop = get_default_shop()
    supplier = get_default_supplier()

    product_data = {
        "t-shirt": {
            "colors": ["black", "yellow"],
        },
        "hoodie": {
            "colors": ["black"],
        }
    }
    for key, data in six.iteritems(product_data):
        parent = create_product(key, shop=shop)
        shop_parent_product = parent.get_shop_instance(shop)
        for color in data["colors"]:
            sku = "%s-%s" % (key, color)
            shop_product = ShopProduct.objects.filter(product__sku=sku).first()
            if shop_product:
                shop_product.suppliers.add(supplier)
            else:
                child = create_product(sku, shop=shop, supplier=supplier)
                child.link_to_parent(parent, variables={"color": color})

    assert Product.objects.count()  == 5

    black_t_shirt = Product.objects.filter(sku="t-shirt-black").first()
    black_hoodie = Product.objects.filter(sku="hoodie-black").first()
    order = create_order_with_product(black_t_shirt, supplier, quantity=1, taxless_base_unit_price=6, shop=shop)
    add_product_to_order(order, supplier, black_hoodie, quantity=1, taxless_base_unit_price=6)

    return black_t_shirt.variation_parent, black_hoodie.variation_parent
Esempio n. 6
0
def test_complex_order_tax(include_taxes):
    tax = get_default_tax()
    quantities = [44, 23, 65]
    product = get_default_product()
    supplier = get_default_supplier()
    shop = get_default_shop()
    shop.prices_include_tax = include_taxes
    shop.save()

    order = create_empty_order(shop=shop)
    order.full_clean()
    order.save()

    pricing_context = get_pricing_module().get_context_from_data(
        shop=shop,
        customer=order.customer or AnonymousContact(),
    )

    total_price = Decimal("0")
    price = Decimal("50")

    for quantity in quantities:
        total_price += quantity * price
        add_product_to_order(order, supplier, product, quantity, price, tax.rate, pricing_context)
    order.cache_prices()
    order.save()

    currency = "EUR"
    summary = order.get_tax_summary()[0]

    assert summary.tax_rate == tax.rate
    assert summary.based_on == Money(total_price, currency)
    assert summary.tax_amount == Money(total_price * tax.rate, currency)
    assert summary.taxful == summary.based_on + summary.tax_amount
    assert order.get_total_tax_amount() == Money(total_price * tax.rate, currency)
Esempio n. 7
0
def test_copy_order_to_basket(admin_user, settings):
    configure(settings)
    shop = factories.get_default_shop()
    basket = factories.get_basket()
    p1 = factories.create_product("test", shop=shop, supplier=factories.get_default_supplier())
    order = factories.create_order_with_product(factories.get_default_product(), factories.get_default_supplier(), 2, 10, shop=shop)
    factories.add_product_to_order(order, factories.get_default_supplier(), p1, 2, 5)
    order.customer = get_person_contact(admin_user)
    order.save()
    client = _get_client(admin_user)
    payload = {
        "order": order.pk
    }
    response = client.post('/api/shuup/basket/{}-{}/add_from_order/'.format(shop.pk, basket.key), payload)
    assert response.status_code == status.HTTP_200_OK
    response_data = json.loads(response.content.decode("utf-8"))
    assert len(response_data["items"]) == 2
    assert not response_data["validation_errors"]
    basket.refresh_from_db()
    assert len(basket.data["lines"]) == 2

    # do it again, basket should clear first then read items
    payload = {
        "order": order.pk
    }
    response = client.post('/api/shuup/basket/{}-{}/add_from_order/'.format(shop.pk, basket.key), payload)
    assert response.status_code == status.HTTP_200_OK
    response_data = json.loads(response.content.decode("utf-8"))
    assert len(response_data["items"]) == 2
    assert not response_data["validation_errors"]
    basket.refresh_from_db()
    assert len(basket.data["lines"]) == 2
Esempio n. 8
0
def test_shipment_with_unshippable_products():
    shop = get_default_shop()
    supplier = get_default_supplier()

    product = create_product(
        "unshippable",
        shop=shop,
        supplier=supplier,
        default_price=5.55)
    product.shipping_mode = ShippingMode.NOT_SHIPPED
    product.save()
    order = _get_order(shop, supplier, stocked=False)
    initial_product_line_count = order.lines.products().count()
    add_product_to_order(order, supplier, product, quantity=4, taxless_base_unit_price=3)
    order.cache_prices()
    order.check_all_verified()
    order.save()

    assert order.shipments.count() == 0
    assert order.can_create_shipment()
    assert not order.can_set_complete()
    order.create_shipment_of_all_products(supplier=supplier)
    assert (order.lines.products().count() == initial_product_line_count + 1)

    assert order.shipments.count() == 1
    assert ShipmentProduct.objects.filter(shipment__order_id=order.id).count() == initial_product_line_count
    assert order.shipping_status == ShippingStatus.FULLY_SHIPPED
    assert order.can_set_complete()
Esempio n. 9
0
def test_complex_order_tax(include_taxes):
    tax = get_default_tax()
    quantities = [44, 23, 65]
    product = get_default_product()
    supplier = get_default_supplier()
    shop = get_default_shop()
    shop.prices_include_tax = include_taxes
    shop.save()

    order = create_empty_order(shop=shop)
    order.full_clean()
    order.save()

    pricing_context = get_pricing_module().get_context_from_data(
        shop=shop,
        customer=order.customer or AnonymousContact(),
    )

    total_price = Decimal("0")
    price = Decimal("50")

    for quantity in quantities:
        total_price += quantity * price
        add_product_to_order(order, supplier, product, quantity, price, tax.rate, pricing_context)
    order.cache_prices()
    order.save()

    currency = "EUR"
    summary = order.get_tax_summary()[0]

    assert summary.tax_rate == tax.rate
    assert summary.based_on == Money(total_price, currency)
    assert summary.tax_amount == Money(total_price * tax.rate, currency)
    assert summary.taxful == summary.based_on + summary.tax_amount
    assert order.get_total_tax_amount() == Money(total_price * tax.rate, currency)
def _init_test_with_variations():
    shop = get_default_shop()
    supplier = get_default_supplier()

    product_data = {
        "t-shirt": {
            "colors": ["black", "yellow"],
        },
        "hoodie": {
            "colors": ["black"],
        },
    }
    for key, data in six.iteritems(product_data):
        parent = create_product(key, shop=shop)
        shop_parent_product = parent.get_shop_instance(shop)
        for color in data["colors"]:
            sku = "%s-%s" % (key, color)
            shop_product = ShopProduct.objects.filter(product__sku=sku).first()
            if shop_product:
                shop_product.suppliers.add(supplier)
            else:
                child = create_product(sku, shop=shop, supplier=supplier)
                child.link_to_parent(parent, variables={"color": color})

    assert Product.objects.count() == 5

    black_t_shirt = Product.objects.filter(sku="t-shirt-black").first()
    black_hoodie = Product.objects.filter(sku="hoodie-black").first()
    order = create_order_with_product(black_t_shirt, supplier, quantity=1, taxless_base_unit_price=6, shop=shop)
    add_product_to_order(order, supplier, black_hoodie, quantity=1, taxless_base_unit_price=6)

    return black_t_shirt, black_hoodie
Esempio n. 11
0
def test_copy_order_to_basket(admin_user, settings):
    configure(settings)
    shop = factories.get_default_shop()
    basket = factories.get_basket()
    p1 = factories.create_product("test", shop=shop, supplier=factories.get_default_supplier())
    order = factories.create_order_with_product(factories.get_default_product(), factories.get_default_supplier(), 2, 10, shop=shop)
    factories.add_product_to_order(order, factories.get_default_supplier(), p1, 2, 5)
    order.customer = get_person_contact(admin_user)
    order.save()
    client = _get_client(admin_user)
    payload = {
        "order": order.pk
    }
    response = client.post('/api/shuup/basket/{}-{}/add_from_order/'.format(shop.pk, basket.key), payload)
    assert response.status_code == status.HTTP_200_OK
    response_data = json.loads(response.content.decode("utf-8"))
    assert len(response_data["items"]) == 2
    assert not response_data["validation_errors"]
    basket.refresh_from_db()
    assert len(basket.data["lines"]) == 2

    # do it again, basket should clear first then read items
    payload = {
        "order": order.pk
    }
    response = client.post('/api/shuup/basket/{}-{}/add_from_order/'.format(shop.pk, basket.key), payload)
    assert response.status_code == status.HTTP_200_OK
    response_data = json.loads(response.content.decode("utf-8"))
    assert len(response_data["items"]) == 2
    assert not response_data["validation_errors"]
    basket.refresh_from_db()
    assert len(basket.data["lines"]) == 2
Esempio n. 12
0
def _add_product_to_order(order, sku, quantity, shop, supplier):
    product = create_product(
        sku=sku,
        shop=shop,
        supplier=supplier,
        default_price=3.33,
    )
    add_product_to_order(order, supplier, product, quantity=quantity, taxless_base_unit_price=1)
Esempio n. 13
0
def _create_order(shop, customer):
    p1 = factories.create_product("test", shop=shop, supplier=factories.get_default_supplier())
    order = factories.create_order_with_product(factories.get_default_product(), factories.get_default_supplier(), 2,
                                                10, shop=shop)
    factories.add_product_to_order(order, factories.get_default_supplier(), p1, 2, 5)
    order.customer = customer
    order.save()
    return order
Esempio n. 14
0
def _create_order(shop, customer):
    p1 = factories.create_product("test", shop=shop, supplier=factories.get_default_supplier())
    order = factories.create_order_with_product(factories.get_default_product(), factories.get_default_supplier(), 2,
                                                10, shop=shop)
    factories.add_product_to_order(order, factories.get_default_supplier(), p1, 2, 5)
    order.customer = customer
    order.save()
    return order
Esempio n. 15
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)
Esempio n. 16
0
def test_order_refunds_with_other_lines(rf, admin_user):
    shop = get_default_shop()
    supplier = get_default_supplier()
    supplier.shops.add(shop)

    product = create_product("sku", shop=shop, default_price=10)
    shop_product = product.get_shop_instance(shop=shop)
    shop_product.suppliers = [supplier]

    order = create_empty_order(shop=shop)
    order.full_clean()
    order.save()

    add_product_to_order(order, supplier, product, 4, 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)

    # Lines with quantity again should be able to be refunded normally.
    other_line_with_quantity = OrderLine(order=order,
                                         type=OrderLineType.OTHER,
                                         text="Special service 100$/h",
                                         quantity=1,
                                         base_unit_price_value=100)
    other_line_with_quantity.save()
    order.lines.add(other_line_with_quantity)

    assert other_line_with_quantity.max_refundable_quantity == 1
    assert other_line_with_quantity.max_refundable_amount.value == 100

    order.cache_prices()
    order.create_payment(order.taxful_total_price)
    assert order.is_paid()
    assert order.taxful_total_price_value == 120  # 100 + 4 * 20

    def get_refund_view_content():
        request = apply_request_middleware(rf.get("/"), user=admin_user)
        view = OrderCreateRefundView.as_view()
        response = view(request, pk=order.pk)
        return BeautifulSoup(response.render().content)

    refund_soup = get_refund_view_content()
    refund_options = refund_soup.find(
        id="id_form-0-line_number").findAll("option")
    assert len(
        refund_options) == 4  # 1 empty line, 1 for arbitrary and 2 for lines
    assert len([
        option for option in refund_options
        if "Special service 100$/h" in force_text(option)
    ]) == 1
Esempio n. 17
0
def _get_order(shop, supplier, stocked=False):
    order = create_empty_order(shop=shop)
    order.full_clean()
    order.save()
    for product_data in _get_product_data(stocked):
        quantity = product_data.pop("quantity")
        product = create_product(
            sku=product_data.pop("sku"), shop=shop, supplier=supplier, default_price=3.33, **product_data
        )
        add_product_to_order(order, supplier, product, quantity=quantity, taxless_base_unit_price=1)
    order.cache_prices()
    order.check_all_verified()
    order.save()
    return order
Esempio n. 18
0
def _get_order(shop, supplier, stocked=False):
    order = create_empty_order(shop=shop)
    order.full_clean()
    order.save()
    for product_data in _get_product_data(stocked):
        quantity = product_data.pop("quantity")
        product = create_product(
            sku=product_data.pop("sku"),
            shop=shop,
            supplier=supplier,
            default_price=3.33,
            **product_data)
        add_product_to_order(order, supplier, product, quantity=quantity, taxless_base_unit_price=1)
    order.cache_prices()
    order.check_all_verified()
    order.save()
    return order
def test_product_relations_max_quantity(rf):
    shop = get_default_shop()
    supplier = get_default_supplier()
    product = create_product("simple-test-product", shop)
    quantities = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
    for i, quantity in enumerate(quantities):
        order = create_order_with_product(product, supplier, quantity=1, taxless_base_unit_price=6, shop=shop)
        add_product_to_order(
            order, supplier, create_product("product-%s" % i, shop), quantity=quantity, taxless_base_unit_price=6
        )

    assert ProductCrossSell.objects.count() == 0
    add_bought_with_relations_for_product(product.pk, max_quantity=5)
    assert ProductCrossSell.objects.count() == 5
    # Test that ordering is ok
    assert not ProductCrossSell.objects.filter(weight=1).exists()
    assert ProductCrossSell.objects.filter(weight=11).exists()
Esempio n. 20
0
def test_rounding_with_taxes(prices):
    shop = get_default_shop()
    supplier = get_default_supplier()

    order = create_empty_order(shop=shop)
    order.save()
    product = create_product("test_sku",  shop=shop, supplier=supplier)
    tax_rate = Decimal("0.22222")
    for x, price in enumerate(prices):
        add_product_to_order(
            order, supplier, product, quantity=Decimal("2.22"),
            taxless_base_unit_price=Decimal(price), tax_rate=tax_rate)
    order.cache_prices()
    for x, order_line in enumerate(order.lines.all().order_by("ordering")):
        # Check that total prices calculated from priceful parts still matches
        assert _get_taxless_price(order_line) == order_line.taxless_price
        assert _get_taxful_price(order_line) == order_line.taxful_price
        assert order_line.price == (order_line.base_unit_price * order_line.quantity - order_line.discount_amount)
def _get_order(shop, supplier, has_price):
    order = create_empty_order(shop=shop)
    order.full_clean()
    order.save()
    for product_data in _get_product_data(has_price):
        quantity = product_data.pop("quantity")
        tax_rate = product_data.pop("tax_rate")
        product = create_product(
            sku=product_data.pop("sku"),
            shop=shop,
            supplier=supplier,
            **product_data)
        add_product_to_order(
            order, supplier, product, quantity=quantity,
            taxless_base_unit_price=product_data["default_price"], tax_rate=tax_rate)
    order.cache_prices()
    order.check_all_verified()
    order.save()
    return order
Esempio n. 22
0
def _get_order(shop, supplier, has_price):
    order = create_empty_order(shop=shop)
    order.full_clean()
    order.save()
    for product_data in _get_product_data(has_price):
        quantity = product_data.pop("quantity")
        tax_rate = product_data.pop("tax_rate")
        product = create_product(
            sku=product_data.pop("sku"),
            shop=shop,
            supplier=supplier,
            **product_data)
        add_product_to_order(
            order, supplier, product, quantity=quantity,
            taxless_base_unit_price=product_data["default_price"], tax_rate=tax_rate)
    order.cache_prices()
    order.check_all_verified()
    order.save()
    return order
def test_order_refunds_with_other_lines():
    shop = get_default_shop()
    supplier = Supplier.objects.create(identifier="1", name="supplier1")
    supplier.shops.add(shop)

    product = create_product("sku", shop=shop, default_price=10)
    shop_product = product.get_shop_instance(shop=shop)
    shop_product.suppliers.set([supplier])

    order = create_empty_order(shop=shop)
    order.full_clean()
    order.save()

    add_product_to_order(order, supplier, product, 4, 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)

    # Lines with quantity again should be able to be refunded normally.
    other_line_with_quantity = OrderLine(order=order,
                                         type=OrderLineType.OTHER,
                                         text="Special service 100$/h",
                                         quantity=1,
                                         base_unit_price_value=100)
    other_line_with_quantity.save()
    order.lines.add(other_line_with_quantity)

    assert other_line_with_quantity.max_refundable_quantity == 1
    assert other_line_with_quantity.max_refundable_amount.value == 100

    order.cache_prices()
    order.create_payment(order.taxful_total_price)
    assert order.is_paid()
    assert order.taxful_total_price_value == 120  # 100 + 4 * 20

    order.create_full_refund()
    assert order.taxful_total_price_value == 0
def test_product_relations_max_quantity(rf):
    shop = get_default_shop()
    supplier = get_default_supplier()
    product = create_product("simple-test-product", shop)
    quantities = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
    for i, quantity in enumerate(quantities):
        order = create_order_with_product(product, supplier, quantity=1, taxless_base_unit_price=6, shop=shop)
        add_product_to_order(
            order,
            supplier,
            create_product("product-%s" % i, shop),
            quantity=quantity,
            taxless_base_unit_price=6
        )

    assert ProductCrossSell.objects.count() == 0
    add_bought_with_relations_for_product(product.pk, max_quantity=5)
    assert ProductCrossSell.objects.count() == 5
    # Test that ordering is ok
    assert not ProductCrossSell.objects.filter(weight=1).exists()
    assert ProductCrossSell.objects.filter(weight=11).exists()
Esempio n. 25
0
def test_order_with_only_unshippable_products():
    shop = get_default_shop()
    supplier = get_default_supplier()

    order = create_empty_order(shop=shop)
    order.full_clean()
    order.save()

    product = create_product(
        "unshippable",
        shop=shop,
        supplier=supplier,
        default_price=5.55)
    product.shipping_mode = ShippingMode.NOT_SHIPPED
    product.save()
    add_product_to_order(order, supplier, product, quantity=4, taxless_base_unit_price=3)
    order.cache_prices()
    order.check_all_verified()
    order.save()

    assert not order.can_create_shipment()
    assert order.can_set_complete()
def test_computing_simple_product_relations(rf):
    shop = get_default_shop()
    supplier = get_default_supplier()
    product = create_product("simple-test-product", shop)
    related_product = create_product("simple-related-product", shop)
    quantities = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
    for quantity in quantities:
        order = create_order_with_product(product, supplier, quantity=1, taxless_base_unit_price=6, shop=shop)
        add_product_to_order(order, supplier, related_product, quantity=quantity, taxless_base_unit_price=6)

    assert ProductCrossSell.objects.count() == 0
    add_bought_with_relations_for_product(product.pk)
    assert ProductCrossSell.objects.count() == 1
    cross_sell_product = ProductCrossSell.objects.filter(product1=product).first()
    assert cross_sell_product.product2 == related_product
    assert cross_sell_product.weight == sum(quantities)

    add_bought_with_relations_for_product(related_product.id)
    assert ProductCrossSell.objects.count() == 2
    cross_sell_product = ProductCrossSell.objects.filter(product1=related_product).first()
    assert cross_sell_product.product2 == product
    assert cross_sell_product.weight == len(quantities)
Esempio n. 27
0
def create_multi_supplier_order_to_review(shop_product, customer):
    order = factories.create_empty_order(shop=shop_product.shop)
    order.full_clean()
    order.save()

    pricing_context = factories._get_pricing_context(order.shop,
                                                     order.customer)
    for supplier in shop_product.suppliers.all():
        factories.add_product_to_order(order, supplier, shop_product.product,
                                       1, shop_product.default_price_value, 0,
                                       pricing_context)

    order.cache_prices()
    order.save()

    order.customer = customer
    order.create_payment(order.taxful_total_price)
    for supplier in shop_product.suppliers.all():
        order.create_shipment_of_all_products(supplier)
    order.status = OrderStatus.objects.get_default_complete()
    order.save()

    return order
def test_order_shipment_section(rf, admin_user):
    shop = get_default_shop()
    supplier1 = Supplier.objects.create(identifier="1", name="supplier1")
    supplier1.shops.add(shop)
    supplier2 = Supplier.objects.create(identifier="2")
    supplier2.shops.add(shop)

    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 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

    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

    set_permissions_for_group(group, ["order.create-shipment", "order.delete-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()) == 3

    # 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)

    # 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)
Esempio n. 29
0
def test_order_refunds_with_multiple_suppliers():
    shop = get_default_shop()
    supplier1 = Supplier.objects.create(identifier="1", name="supplier1")
    supplier1.shops.add(shop)
    supplier2 = Supplier.objects.create(identifier="2")
    supplier2.shops.add(shop)
    supplier3 = Supplier.objects.create(identifier="3", name="s")
    supplier3.shops.add(shop)

    product1 = create_product("sku1", shop=shop, default_price=10)
    shop_product1 = product1.get_shop_instance(shop=shop)
    shop_product1.suppliers = [supplier1, supplier2, supplier3]

    product2 = create_product("sku2", shop=shop, default_price=10)
    shop_product2 = product1.get_shop_instance(shop=shop)
    shop_product2.suppliers = [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 = [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)

    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

    order.create_refund([{
        "line": "amount",
        "quantity": 1,
        "amount": order.shop.create_price(200)
    }], supplier=supplier3)
    assert OrderLine.objects.filter(order=order, supplier=supplier3, type=OrderLineType.REFUND).exists()

    # Supplier 3 refunds the order
    order.create_refund(_get_refund_data(order, supplier3))
    assert order.get_total_refunded_amount(supplier3).value  == Decimal("255")  # 11 * 5
    assert order.get_total_unrefunded_amount(supplier3).value == Decimal("0")
    assert not order.can_create_refund(supplier1)
    assert not order.can_create_refund(supplier2)
    assert not order.can_create_refund(supplier3)
    assert not order.can_create_refund()
def test_refunds_with_multiple_suppliers(rf, admin_user):
    shop = get_default_shop()
    supplier1 = Supplier.objects.create(identifier="1", name="supplier1")
    supplier1.shops.add(shop)

    supplier2 = Supplier.objects.create(identifier="2")
    supplier2.shops.add(shop)

    supplier3 = Supplier.objects.create(identifier="3", name="s")
    supplier3.shops.add(shop)

    product1 = create_product("sku1", shop=shop, default_price=10)
    shop_product1 = product1.get_shop_instance(shop=shop)
    shop_product1.suppliers = [supplier1, supplier2, supplier3]

    product2 = create_product("sku2", shop=shop, default_price=10)
    shop_product2 = product1.get_shop_instance(shop=shop)
    shop_product2.suppliers = [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 = [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)

    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 not order.has_refunds()
    assert len(order.lines.all()) == 6

    product_line = order.lines.first()
    data = {
        "form-0-line_number": 0,
        "form-0-quantity": 1,
        "form-0-amount": 1,
        "form-0-restock_products": False,
        "form-INITIAL_FORMS": 0,
        "form-MAX_NUM_FORMS": 1000,
        "form-TOTAL_FORMS": 1,
        "form-MIN_NUM_FORMS": 0,
    }

    supplier_provider = "shuup.testing.supplier_provider.RequestSupplierProvider"
    with override_settings(SHUUP_ADMIN_SUPPLIER_PROVIDER_SPEC=supplier_provider):
        # Test refund view content as superuser
        request = apply_request_middleware(rf.get("/", data=data), user=admin_user)
        view = OrderCreateRefundView.as_view()
        response = view(request, pk=order.pk)
        assert response.status_code == 200
        if hasattr(response, "render"):
            response.render()

        soup = BeautifulSoup(response.content)
        _assert_order_table_row_count(soup, 6)
        _assert_order_mobile_list_row_count(soup, 6)
        assert _get_create_full_refund_button(soup) is not None

        # Test refund view content as supplier 1
        request = apply_request_middleware(rf.get("/", data=data), user=admin_user)
        request.supplier = supplier1
        assert get_supplier(request) == supplier1

        view = OrderCreateRefundView.as_view()
        response = view(request, pk=order.pk)
        assert response.status_code == 200
        if hasattr(response, "render"):
            response.render()

        soup = BeautifulSoup(response.content)
        _assert_order_table_row_count(soup, 2)
        _assert_order_mobile_list_row_count(soup, 2)
        assert _get_create_full_refund_button(soup) is None

        # Test refund view content as supplier 2 user
        request = apply_request_middleware(rf.get("/", data=data), user=admin_user)
        request.supplier = supplier2
        assert get_supplier(request) == supplier2

        view = OrderCreateRefundView.as_view()
        response = view(request, pk=order.pk)
        assert response.status_code == 200
        if hasattr(response, "render"):
            response.render()

        soup = BeautifulSoup(response.content)
        _assert_order_table_row_count(soup, 2)
        _assert_order_mobile_list_row_count(soup, 2)
        assert _get_create_full_refund_button(soup) is None
Esempio n. 31
0
def test_refunds_with_multiple_suppliers(rf, admin_user):
    shop = get_default_shop()
    supplier1 = Supplier.objects.create(identifier="1", name="supplier1")
    supplier1.shops.add(shop)

    supplier2 = Supplier.objects.create(identifier="2")
    supplier2.shops.add(shop)

    supplier3 = Supplier.objects.create(identifier="3", name="s")
    supplier3.shops.add(shop)

    product1 = create_product("sku1", shop=shop, default_price=10)
    shop_product1 = product1.get_shop_instance(shop=shop)
    shop_product1.suppliers = [supplier1, supplier2, supplier3]

    product2 = create_product("sku2", shop=shop, default_price=10)
    shop_product2 = product1.get_shop_instance(shop=shop)
    shop_product2.suppliers = [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 = [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)

    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 not order.has_refunds()
    assert len(order.lines.all()) == 6

    product_line = order.lines.first()
    data = {
        "form-0-line_number": 0,
        "form-0-quantity": 1,
        "form-0-amount": 1,
        "form-0-restock_products": False,
        "form-INITIAL_FORMS": 0,
        "form-MAX_NUM_FORMS": 1000,
        "form-TOTAL_FORMS": 1,
        "form-MIN_NUM_FORMS": 0,
    }

    supplier_provider = "shuup.testing.supplier_provider.RequestSupplierProvider"
    with override_settings(
            SHUUP_ADMIN_SUPPLIER_PROVIDER_SPEC=supplier_provider):
        # Test refund view content as superuser
        request = apply_request_middleware(rf.get("/", data=data),
                                           user=admin_user)
        view = OrderCreateRefundView.as_view()
        response = view(request, pk=order.pk)
        assert response.status_code == 200
        if hasattr(response, "render"):
            response.render()

        soup = BeautifulSoup(response.content)
        _assert_order_table_row_count(soup, 6)
        _assert_order_mobile_list_row_count(soup, 6)
        assert _get_create_full_refund_button(soup) is not None

        # Test refund view content as supplier 1
        request = apply_request_middleware(rf.get("/", data=data),
                                           user=admin_user)
        request.supplier = supplier1
        assert get_supplier(request) == supplier1

        view = OrderCreateRefundView.as_view()
        response = view(request, pk=order.pk)
        assert response.status_code == 200
        if hasattr(response, "render"):
            response.render()

        soup = BeautifulSoup(response.content)
        _assert_order_table_row_count(soup, 2)
        _assert_order_mobile_list_row_count(soup, 2)
        assert _get_create_full_refund_button(soup) is None

        # Test refund view content as supplier 2 user
        request = apply_request_middleware(rf.get("/", data=data),
                                           user=admin_user)
        request.supplier = supplier2
        assert get_supplier(request) == supplier2

        view = OrderCreateRefundView.as_view()
        response = view(request, pk=order.pk)
        assert response.status_code == 200
        if hasattr(response, "render"):
            response.render()

        soup = BeautifulSoup(response.content)
        _assert_order_table_row_count(soup, 2)
        _assert_order_mobile_list_row_count(soup, 2)
        assert _get_create_full_refund_button(soup) is None
Esempio n. 32
0
def test_order_shipments(rf, admin_user):
    shop = get_default_shop()
    supplier1 = Supplier.objects.create(identifier="1", name="supplier1")
    supplier1.shops.add(shop)
    supplier2 = Supplier.objects.create(identifier="2")
    supplier2.shops.add(shop)

    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 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

    assert order.get_sent_shipments().count() == 3
    order.shipments.filter(status=ShipmentStatus.NOT_SENT) == 0

    # Make product1 unshipped
    product1.shipping_mode = ShippingMode.NOT_SHIPPED
    product1.save()

    # We still should see the order shipment section since existing shipments
    assert ShipmentSection.visible_for_object(order, request)

    # list all shipments in shipments list view
    response = client.get("{}?jq={}".format(
        reverse("shuup_admin:order.shipments.list"),
        json.dumps({
            "perPage": 10,
            "page": 1
        })))
    assert response.status_code == 200
    data = json.loads(response.content)
    assert len(data["items"]) == 3
    for item in data["items"]:
        assert item["status"] == "Sent"

    # Let's delete all shipments since both products is unshipped and we
    # don't need those.
    for shipment in order.shipments.all():
        shipment.soft_delete()

    assert not ShipmentSection.visible_for_object(order, request)
def test_order_product_summary_with_multiple_suppliers():
    shop = get_default_shop()
    supplier1 = Supplier.objects.create(identifier="1", name="supplier1")
    supplier1.shops.add(shop)
    supplier2 = Supplier.objects.create(identifier="2")
    supplier2.shops.add(shop)
    supplier3 = Supplier.objects.create(identifier="3", name="s")
    supplier3.shops.add(shop)

    product1 = create_product("sku1", shop=shop, default_price=10)
    shop_product1 = product1.get_shop_instance(shop=shop)
    shop_product1.suppliers = [supplier1, supplier2, supplier3]

    product2 = create_product("sku2", shop=shop, default_price=10)
    shop_product2 = product1.get_shop_instance(shop=shop)
    shop_product2.suppliers = [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 = [supplier3]

    product_quantities = {
        supplier1.pk: {
            product1.pk: 5,
            product2.pk: 6
        },
        supplier2.pk: {
            product1.pk: 3,
            product2.pk: 13
        },
        supplier3.pk: {
            product1.pk: 1,
            product3.pk: 50
        }
    }

    def get_quantity(supplier, product):
        return product_quantities[supplier.pk][product.pk]

    order = create_empty_order(shop=shop)
    order.full_clean()
    order.save()

    # Add product 3 to order for supplier 3
    add_product_to_order(order, supplier3, product3,
                         get_quantity(supplier3, product3), 8)
    assert order.get_product_ids_and_quantities()[product3.pk] == 50
    assert not order.has_products_requiring_shipment()
    assert not order.has_products_requiring_shipment(supplier3)

    # Add product 2 to order for supplier 1
    add_product_to_order(order, supplier1, product2,
                         get_quantity(supplier1, product2), 7)
    assert order.get_product_ids_and_quantities()[product2.pk] == 6
    assert order.has_products_requiring_shipment()
    assert order.has_products_requiring_shipment(supplier1)
    assert not order.has_products_requiring_shipment(supplier3)

    # Add product 2 to order for supplier 2
    add_product_to_order(order, supplier2, product2,
                         get_quantity(supplier2, product2), 6)
    assert order.get_product_ids_and_quantities()[product2.pk] == 19
    assert order.has_products_requiring_shipment()
    assert order.has_products_requiring_shipment(supplier1)
    assert order.has_products_requiring_shipment(supplier2)
    assert not order.has_products_requiring_shipment(supplier3)

    # Add product 1 to order for supplier 3
    add_product_to_order(order, supplier3, product1,
                         get_quantity(supplier3, product1), 5)
    assert order.get_product_ids_and_quantities()[product1.pk] == 1
    assert order.has_products_requiring_shipment()
    assert order.has_products_requiring_shipment(supplier1)
    assert order.has_products_requiring_shipment(supplier2)
    assert order.has_products_requiring_shipment(supplier3)

    # Add product 1 for supplier 1 and 3
    add_product_to_order(order, supplier1, product1,
                         get_quantity(supplier1, product1), 4)
    add_product_to_order(order, supplier2, product1,
                         get_quantity(supplier2, product1), 3)
    assert order.get_product_ids_and_quantities()[product1.pk] == 9

    product_summary = order.get_product_summary()
    _assert_product_summary(product_summary, product1.pk, 9, 9, 0, 0)
    _assert_product_summary(product_summary, product2.pk, 19, 19, 0, 0)
    _assert_product_summary(product_summary, product3.pk, 50, 0, 0, 0)

    # Test product summary per supplier
    product_summary = order.get_product_summary(supplier1)
    _assert_product_summary(product_summary, product1.pk, 5, 5, 0, 0)
    _assert_product_summary(product_summary, product2.pk, 6, 6, 0, 0)
    _assert_product_summary(product_summary, product3.pk, 0, 0, 0, 0)

    product_summary = order.get_product_summary(supplier2.pk)
    _assert_product_summary(product_summary, product1.pk, 3, 3, 0, 0)
    _assert_product_summary(product_summary, product2.pk, 13, 13, 0, 0)
    _assert_product_summary(product_summary, product3.pk, 0, 0, 0, 0)

    product_summary = order.get_product_summary(supplier3.pk)
    _assert_product_summary(product_summary, product1.pk, 1, 1, 0, 0)
    _assert_product_summary(product_summary, product2.pk, 0, 0, 0, 0)
    _assert_product_summary(product_summary, product3.pk, 50, 0, 0, 0)

    # 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()

    # Let's make suer all good with unshipped products
    unshipped_products = order.get_unshipped_products()
    assert unshipped_products[product1.pk]["unshipped"] == 9
    assert unshipped_products[product2.pk]["unshipped"] == 19
    assert unshipped_products.get(product3.pk) is None

    unshipped_products = order.get_unshipped_products(supplier1)
    assert unshipped_products[product1.pk]["unshipped"] == 5
    assert unshipped_products[product2.pk]["unshipped"] == 6

    unshipped_products = order.get_unshipped_products(supplier2)
    assert unshipped_products[product1.pk]["unshipped"] == 3
    assert unshipped_products[product2.pk]["unshipped"] == 13

    unshipped_products = order.get_unshipped_products(supplier3)
    assert unshipped_products[product1.pk]["unshipped"] == 1
    assert unshipped_products.get(product2.pk) is None
    assert unshipped_products.get(product3.pk) is None

    # Refund product3
    line_to_refund = order.lines.filter(product_id=product3.pk).first()
    order.create_refund([{
        "line": line_to_refund,
        "quantity": 10,
        "amount": shop.create_price("10").amount
    }])
    product_summary = order.get_product_summary()
    _assert_product_summary(product_summary, product3.pk, 50, 0, 0, 10)
    product_summary = order.get_product_summary(supplier3.pk)
    _assert_product_summary(product_summary, product3.pk, 50, 0, 0, 10)

    order.create_refund([{
        "line": line_to_refund,
        "quantity": 40,
        "amount": shop.create_price("20").amount
    }])
    product_summary = order.get_product_summary()
    _assert_product_summary(product_summary, product3.pk, 50, 0, 0, 50)
    product_summary = order.get_product_summary(supplier3.pk)
    _assert_product_summary(product_summary, product3.pk, 50, 0, 0, 50)

    # Then ship product 1 for all suppliers one by one
    order.create_shipment({product1: 1}, supplier=supplier3)
    unshipped_products = order.get_unshipped_products(supplier3)
    assert unshipped_products.get(product1.pk) is None
    unshipped_products = order.get_unshipped_products()
    assert unshipped_products[product1.pk]["unshipped"] == 8

    order.create_shipment({product1: 3}, supplier=supplier2)
    unshipped_products = order.get_unshipped_products(supplier2)
    assert unshipped_products.get(product1.pk) is None
    unshipped_products = order.get_unshipped_products()
    assert unshipped_products[product1.pk]["unshipped"] == 5

    order.create_shipment({product1: 5}, supplier=supplier1)
    unshipped_products = order.get_unshipped_products(supplier1)
    assert unshipped_products.get(product1.pk) is None
    unshipped_products = order.get_unshipped_products()
    assert unshipped_products.get(product1.pk) is None

    # Then ship product 2 for all suppliers with a twist
    order.create_shipment({product2: 13}, supplier=supplier2)
    unshipped_products = order.get_unshipped_products(supplier2)
    assert unshipped_products.get(product2.pk) is None
    unshipped_products = order.get_unshipped_products()
    assert unshipped_products[product2.pk]["unshipped"] == 6

    order.create_shipment({product2: 5}, supplier=supplier1)
    unshipped_products = order.get_unshipped_products(supplier1)
    assert unshipped_products[product2.pk]["unshipped"] == 1
    unshipped_products = order.get_unshipped_products()
    assert unshipped_products[product2.pk]["unshipped"] == 1
    product_summary = order.get_product_summary()
    _assert_product_summary(product_summary, product2.pk, 19, 1, 18, 0)

    # Refund the last product and see all falling in place
    line_to_refund = order.lines.filter(product_id=product2.pk,
                                        supplier=supplier1).first()
    order.create_refund([{
        "line": line_to_refund,
        "quantity": 1,
        "amount": shop.create_price("1").amount
    }])

    assert not order.get_unshipped_products()
    assert order.is_fully_shipped()

    # Verify product summary
    product_summary = order.get_product_summary()
    _assert_product_summary(product_summary,
                            product1.pk,
                            9,
                            0,
                            9,
                            0,
                            suppliers=[supplier1, supplier2, supplier3])
    _assert_product_summary(product_summary,
                            product2.pk,
                            19,
                            0,
                            18,
                            1,
                            suppliers=[supplier1, supplier2])
    _assert_product_summary(product_summary,
                            product3.pk,
                            50,
                            0,
                            0,
                            50,
                            suppliers=[supplier3])

    # Order prodcuts and quantities still match, right?
    order_products_and_quantities = order.get_product_ids_and_quantities()
    assert order_products_and_quantities[product1.pk] == 9
    assert order_products_and_quantities[product2.pk] == 19
    assert order_products_and_quantities[product3.pk] == 50

    # Order still has products requiring shipments, right?
    assert order.has_products_requiring_shipment()
    assert order.has_products_requiring_shipment(supplier1)
    assert order.has_products_requiring_shipment(supplier2)
    assert order.has_products_requiring_shipment(supplier3)
Esempio n. 34
0
def test_order_refunds_with_multiple_suppliers():
    shop = get_default_shop()
    supplier1 = Supplier.objects.create(identifier="1", name="supplier1")
    supplier1.shops.add(shop)
    supplier2 = Supplier.objects.create(identifier="2")
    supplier2.shops.add(shop)
    supplier3 = Supplier.objects.create(identifier="3", name="s")
    supplier3.shops.add(shop)

    product1 = create_product("sku1", shop=shop, default_price=10)
    shop_product1 = product1.get_shop_instance(shop=shop)
    shop_product1.suppliers.set([supplier1, supplier2, supplier3])

    product2 = create_product("sku2", shop=shop, default_price=10)
    shop_product2 = product1.get_shop_instance(shop=shop)
    shop_product2.suppliers.set([supplier1, supplier2])

    product3 = create_product("sku3", shop=shop, default_price=10, shipping_mode=ShippingMode.NOT_SHIPPED)
    shop_product3 = product1.get_shop_instance(shop=shop)
    shop_product3.suppliers.set([supplier3])

    product_quantities = {
        supplier1: {product1: 5, product2: 6},
        supplier2: {product1: 3, product2: 13},
        supplier3: {product1: 1, product3: 50},
    }

    def get_quantity(supplier, product):
        return product_quantities[supplier.pk][product.pk]

    order = create_empty_order(shop=shop)
    order.full_clean()
    order.save()

    for supplier, product_data in six.iteritems(product_quantities):
        for product, quantity in six.iteritems(product_data):
            add_product_to_order(order, supplier, product, quantity, 5)

    # Lines without quantity shouldn't affect refunds
    other_line = OrderLine(
        order=order, type=OrderLineType.OTHER, text="This random line for textual information", quantity=0
    )
    other_line.save()
    order.lines.add(other_line)

    order.cache_prices()
    order.create_payment(order.taxful_total_price)
    assert order.is_paid()

    # All supplier should be able to refund the order
    assert order.can_create_refund()
    assert order.can_create_refund(supplier1)
    assert order.can_create_refund(supplier2)
    assert order.can_create_refund(supplier3)

    assert order.get_total_unrefunded_amount(supplier1).value == Decimal("55")  # 11 * 5
    assert order.get_total_unrefunded_quantity(supplier1) == Decimal("11")  # 5 x product1 and 6 x product2

    with pytest.raises(RefundExceedsAmountException):
        order.create_refund(
            [{"line": "amount", "quantity": 1, "amount": order.shop.create_price(60)}], supplier=supplier1
        )

    # Supplier 1 refunds the order
    order.create_refund(_get_refund_data(order, supplier1))
    assert order.get_total_refunded_amount(supplier1).value == Decimal("55")  # 11 * 5
    assert order.get_total_unrefunded_amount(supplier1).value == Decimal("0")

    assert not order.can_create_refund(supplier1)
    assert order.can_create_refund()
    assert order.can_create_refund(supplier2)
    assert order.can_create_refund(supplier3)

    assert order.get_total_unrefunded_amount(supplier2).value == Decimal("80")  # 16 * 5
    assert order.get_total_unrefunded_quantity(supplier2) == Decimal("16")  # 3 x product1 and 13 x product2

    with pytest.raises(RefundExceedsAmountException):
        order.create_refund(
            [{"line": "amount", "quantity": 1, "amount": order.shop.create_price(81)}], supplier=supplier2
        )

    # Supplier 2 refunds the order
    order.create_refund(_get_refund_data(order, supplier2))
    assert order.get_total_refunded_amount(supplier2).value == Decimal("80")  # 11 * 5
    assert order.get_total_unrefunded_amount(supplier2).value == Decimal("0")
    assert not order.can_create_refund(supplier1)
    assert not order.can_create_refund(supplier2)
    assert order.can_create_refund()
    assert order.can_create_refund(supplier3)

    assert order.get_total_unrefunded_amount(supplier3).value == Decimal("255")  # 51 * 5
    assert order.get_total_unrefunded_quantity(supplier3) == Decimal("51")  # 3 x product1 and 13 x product2

    with override_settings(SHUUP_ALLOW_ARBITRARY_REFUNDS=False):
        with pytest.raises(RefundArbitraryRefundsNotAllowedException):
            order.create_refund(
                [{"line": "amount", "quantity": 1, "amount": order.shop.create_price(200)}], supplier=supplier3
            )

    order.create_refund([{"line": "amount", "quantity": 1, "amount": order.shop.create_price(200)}], supplier=supplier3)
    assert OrderLine.objects.filter(order=order, supplier=supplier3, type=OrderLineType.REFUND).exists()

    # Supplier 3 refunds the order
    order.create_refund(_get_refund_data(order, supplier3))
    assert order.get_total_refunded_amount(supplier3).value == Decimal("255")  # 11 * 5
    assert order.get_total_unrefunded_amount(supplier3).value == Decimal("0")
    assert not order.can_create_refund(supplier1)
    assert not order.can_create_refund(supplier2)
    assert not order.can_create_refund(supplier3)
    assert not order.can_create_refund()
Esempio n. 35
0
def test_order_shipment_section(rf, admin_user):
    shop = get_default_shop()
    supplier1 = Supplier.objects.create(identifier="1", name="supplier1")
    supplier1.shops.add(shop)
    supplier2 = Supplier.objects.create(identifier="2")
    supplier2.shops.add(shop)

    product1 = create_product("sku1", shop=shop, default_price=10)
    shop_product1 = product1.get_shop_instance(shop=shop)
    shop_product1.suppliers = [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 = [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 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

    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

    set_permissions_for_group(group, ["order.create-shipment", "order.delete-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()) == 3

    # 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)

    # 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)
Esempio n. 36
0
def test_get_best_selling_products(admin_user):
    shop1 = get_default_shop()
    shop2 = get_shop(True)
    person1 = create_random_person()
    person1.user = admin_user
    person1.save()

    supplier = create_simple_supplier("supplier1")
    client = _get_client(admin_user)

    # list best selling products
    response = client.get("/api/shuup/front/shop_products/best_selling/", {"shop": shop2.pk, "limit": 20})
    assert response.status_code == status.HTTP_200_OK
    products = json.loads(response.content.decode("utf-8"))
    assert len(products["results"]) == 0

    # THIS IS IMPORTANT!
    cache.clear()

    products = [create_product("Standard-%d" % x, supplier=supplier, shop=shop2) for x in range(10)]

    # create 1 product with 4 variations
    parent_product = create_product("ParentProduct1", supplier=supplier, shop=shop2)
    children = [create_product("SimpleVarChild-%d" % x, supplier=supplier, shop=shop2) for x in range(4)]
    for child in children:
        child.link_to_parent(parent_product)

    best_selling = defaultdict(int)

    # create orders with standard products
    for p_index in range(len(products)):
        order = create_empty_order(shop=shop2)
        order.save()
        qty = (len(products)-p_index)
        add_product_to_order(order, supplier, products[p_index], qty, Decimal(1.0))
        order.create_shipment_of_all_products()
        order.status = OrderStatus.objects.get_default_complete()
        order.save(update_fields=("status",))

        best_selling[products[p_index].id] = qty

    # create orders with variation products - the parent product is counted instead of its children
    for p_index in range(2):
        variation = random.choice(children)
        qty = 5
        order = create_empty_order(shop=shop2)
        order.save()
        add_product_to_order(order, supplier, variation, qty, Decimal(1.0))
        order.create_shipment_of_all_products()
        order.status = OrderStatus.objects.get_default_complete()
        order.save(update_fields=("status",))
        best_selling[parent_product.id] = best_selling[parent_product.id] + qty

    # get the top 100 best selling products
    response = client.get("/api/shuup/front/shop_products/best_selling/", {"shop": shop2.pk, "limit": 100})
    assert response.status_code == status.HTTP_200_OK
    products = json.loads(response.content.decode("utf-8"))
    assert len(products["results"]) == len(best_selling) # as we added less then 100, this must be true
    assert products["next"] is None

    # check the if all IDS are part of best selling
    for ix in range(len(products)):
        assert products["results"][ix]["product_id"] in best_selling.keys()

    # get the top 5 best selling products (we should get paginated results)
    response = client.get("/api/shuup/front/shop_products/best_selling/", {"shop": shop2.pk, "limit": 5})
    assert response.status_code == status.HTTP_200_OK
    products = json.loads(response.content.decode("utf-8"))
    assert len(products["results"]) == 5
    assert products["count"] == len(best_selling)
    assert products["next"] is not None
    sorted_best_selling_ids = [prod[0] for prod in sorted(best_selling.items(), key=lambda prod: -prod[1])][:5]

    # check the if all the 5 best sellers are part of best selling
    for ix in range(len(products)):
        assert products["results"][ix]["product_id"] in sorted_best_selling_ids
Esempio n. 37
0
def test_order_arbitrary_refunds_with_multiple_suppliers():
    shop = get_default_shop()
    supplier1 = Supplier.objects.create(identifier="1", name="supplier1")
    supplier1.shops.add(shop)
    supplier2 = Supplier.objects.create(identifier="2")
    supplier2.shops.add(shop)
    supplier3 = Supplier.objects.create(identifier="3", name="s")
    supplier3.shops.add(shop)

    product1 = create_product("sku1", shop=shop, default_price=10)
    shop_product1 = product1.get_shop_instance(shop=shop)
    shop_product1.suppliers.set([supplier1, supplier2, supplier3])

    product2 = create_product("sku2", shop=shop, default_price=10)
    shop_product2 = product1.get_shop_instance(shop=shop)
    shop_product2.suppliers.set([supplier1, supplier2])

    product3 = create_product("sku3", shop=shop, default_price=10, shipping_mode=ShippingMode.NOT_SHIPPED)
    shop_product3 = product1.get_shop_instance(shop=shop)
    shop_product3.suppliers.set([supplier3])

    product_quantities = {
        supplier1: {product1: 5, product2: 6},
        supplier2: {product1: 3, product2: 13},
        supplier3: {product1: 1, product3: 50},
    }

    def get_quantity(supplier, product):
        return product_quantities[supplier.pk][product.pk]

    order = create_empty_order(shop=shop)
    order.full_clean()
    order.save()

    for supplier, product_data in six.iteritems(product_quantities):
        for product, quantity in six.iteritems(product_data):
            add_product_to_order(order, supplier, product, quantity, 5)

    # Lines without quantity shouldn't affect refunds
    other_line = OrderLine(
        order=order, type=OrderLineType.OTHER, text="This random line for textual information", quantity=0
    )
    other_line.save()
    order.lines.add(other_line)

    order.cache_prices()
    order.create_payment(order.taxful_total_price)
    assert order.is_paid()

    # All supplier should be able to refund the order
    assert order.can_create_refund()
    assert order.can_create_refund(supplier1)
    assert order.can_create_refund(supplier2)
    assert order.can_create_refund(supplier3)

    # Step by step refund lines for supplier1
    assert order.can_create_refund()
    assert order.get_total_unrefunded_amount(supplier1).value == Decimal("55")  # 11 * 5
    assert order.get_total_unrefunded_amount().value == Decimal("390")  # 55 + 80 + 255
    proudct1_line_for_supplier1 = order.lines.filter(supplier=supplier1, product=product1).first()
    supplier1_refund_data = [
        {
            "line": proudct1_line_for_supplier1,
            "quantity": proudct1_line_for_supplier1.quantity,
            "amount": order.shop.create_price(20).amount,  # Line total is 5 * 5 = 25
            "restock_products": True,
        }
    ]
    order.create_refund(supplier1_refund_data)
    assert order.get_total_unrefunded_amount(supplier1).value == Decimal("35")

    order.create_refund(
        [{"line": "amount", "quantity": 1, "amount": order.shop.create_price(30).amount}], supplier=supplier1
    )

    assert order.get_total_unrefunded_amount(supplier1).value == Decimal("5")

    order.create_refund(
        [{"line": "amount", "quantity": 1, "amount": order.shop.create_price(5).amount}], supplier=supplier1
    )

    assert order.get_total_unrefunded_amount(supplier1).value == Decimal("0")
    assert order.can_create_refund(supplier1)  # Some quantity still left to refund

    proudct2_line_for_supplier1 = order.lines.filter(supplier=supplier1, product=product2).first()
    supplier1_restock_refund_data = [
        {
            "line": proudct2_line_for_supplier1,
            "quantity": proudct2_line_for_supplier1.quantity,
            "amount": order.shop.create_price(0).amount,  # Line total is 5 * 5 = 25
            "restock_products": True,
        }
    ]
    order.create_refund(supplier1_restock_refund_data)
    assert not order.can_create_refund(supplier1)

    # Step by step refund lines for supplier2
    assert order.can_create_refund()
    assert order.get_total_unrefunded_amount(supplier2).value == Decimal("80")  # 16 * 5
    assert order.get_total_unrefunded_amount().value == Decimal("335")  # 80 + 255
    proudct2_line_for_supplier2 = order.lines.filter(supplier=supplier2, product=product2).first()
    supplier2_refund_data = [
        {
            "line": proudct2_line_for_supplier2,
            "quantity": 10,
            "amount": order.shop.create_price(50).amount,  # Line total is 13 * 5 = 65
            "restock_products": True,
        }
    ]
    order.create_refund(supplier2_refund_data)
    assert order.get_total_unrefunded_amount(supplier2).value == Decimal("30")

    order.create_refund(
        [{"line": "amount", "quantity": 1, "amount": order.shop.create_price(5).amount}], supplier=supplier2
    )

    assert order.get_total_unrefunded_amount(supplier2).value == Decimal("25")

    order.create_refund(
        [{"line": "amount", "quantity": 1, "amount": order.shop.create_price(25).amount}], supplier=supplier2
    )

    assert order.get_total_unrefunded_amount(supplier2).value == Decimal("0")
    assert order.can_create_refund(supplier2)  # Some quantity still left to refund

    supplier2_restock_refund_data = [
        {
            "line": proudct2_line_for_supplier2,
            "quantity": 3,
            "amount": order.shop.create_price(0).amount,  # Line total is 5 * 5 = 25
            "restock_products": True,
        }
    ]
    order.create_refund(supplier2_restock_refund_data)

    proudct1_line_for_supplier2 = order.lines.filter(supplier=supplier2, product=product1).first()
    supplier1_restock_refund_data = [
        {
            "line": proudct1_line_for_supplier2,
            "quantity": proudct1_line_for_supplier2.quantity,
            "amount": order.shop.create_price(0).amount,  # Line total is 5 * 5 = 25
            "restock_products": True,
        }
    ]
    order.create_refund(supplier1_restock_refund_data)
    assert not order.can_create_refund(supplier2)

    # Step by step refund lines for supplier3
    assert order.can_create_refund()
    assert order.get_total_unrefunded_amount(supplier3).value == Decimal("255")  # 51 * 5
    assert order.get_total_unrefunded_amount().value == Decimal("255")  # 255

    order.create_refund(
        [{"line": "amount", "quantity": 1, "amount": order.shop.create_price(55).amount}], supplier=supplier3
    )

    assert order.get_total_unrefunded_amount(supplier3).value == Decimal("200")

    proudct3_line_for_supplier3 = order.lines.filter(supplier=supplier3, product=product3).first()
    supplier3_refund_data = [
        {
            "line": proudct3_line_for_supplier3,
            "quantity": 50,
            "amount": order.shop.create_price(200).amount,  # Line total is 13 * 5 = 65
            "restock_products": True,
        }
    ]
    order.create_refund(supplier3_refund_data)
    assert order.get_total_unrefunded_amount(supplier2).value == Decimal("0")
    assert order.get_total_unrefunded_amount().value == Decimal("0")
    assert order.can_create_refund(supplier3)  # Some quantity still left to refund

    proudct1_line_for_supplier3 = order.lines.filter(supplier=supplier3, product=product1).first()
    supplier3_restock_refund_data = [
        {
            "line": proudct1_line_for_supplier3,
            "quantity": proudct1_line_for_supplier3.quantity,
            "amount": order.shop.create_price(0).amount,  # Line total is 5 * 5 = 25
            "restock_products": True,
        }
    ]
    order.create_refund(supplier3_restock_refund_data)
    assert not order.can_create_refund(supplier3)
    assert not order.can_create_refund()
def test_get_best_selling_products(admin_user):
    shop1 = get_default_shop()
    shop2 = get_shop(True)
    person1 = create_random_person()
    person1.user = admin_user
    person1.save()

    supplier = create_simple_supplier("supplier1")
    client = _get_client(admin_user)

    # list best selling products
    response = client.get("/api/shuup/front/shop_products/best_selling/", {
        "shop": shop2.pk,
        "limit": 20
    })
    assert response.status_code == status.HTTP_200_OK
    products = json.loads(response.content.decode("utf-8"))
    assert len(products["results"]) == 0

    # THIS IS IMPORTANT!
    cache.clear()

    products = [
        create_product("Standard-%d" % x, supplier=supplier, shop=shop2)
        for x in range(10)
    ]

    # create 1 product with 4 variations
    parent_product = create_product("ParentProduct1",
                                    supplier=supplier,
                                    shop=shop2)
    children = [
        create_product("SimpleVarChild-%d" % x, supplier=supplier, shop=shop2)
        for x in range(4)
    ]
    for child in children:
        child.link_to_parent(parent_product)

    best_selling = defaultdict(int)

    # create orders with standard products
    for p_index in range(len(products)):
        order = create_empty_order(shop=shop2)
        order.save()
        qty = (len(products) - p_index)
        add_product_to_order(order, supplier, products[p_index], qty,
                             Decimal(1.0))
        order.create_shipment_of_all_products()
        order.status = OrderStatus.objects.get_default_complete()
        order.save(update_fields=("status", ))

        best_selling[products[p_index].id] = qty

    # create orders with variation products - the parent product is counted instead of its children
    for p_index in range(2):
        variation = random.choice(children)
        qty = 5
        order = create_empty_order(shop=shop2)
        order.save()
        add_product_to_order(order, supplier, variation, qty, Decimal(1.0))
        order.create_shipment_of_all_products()
        order.status = OrderStatus.objects.get_default_complete()
        order.save(update_fields=("status", ))
        best_selling[parent_product.id] = best_selling[parent_product.id] + qty

    # get the top 100 best selling products
    response = client.get("/api/shuup/front/shop_products/best_selling/", {
        "shop": shop2.pk,
        "limit": 100
    })
    assert response.status_code == status.HTTP_200_OK
    products = json.loads(response.content.decode("utf-8"))
    assert len(products["results"]) == len(
        best_selling)  # as we added less then 100, this must be true
    assert products["next"] is None

    # check the if all IDS are part of best selling
    for ix in range(len(products)):
        assert products["results"][ix]["product_id"] in best_selling.keys()

    # get the top 5 best selling products (we should get paginated results)
    response = client.get("/api/shuup/front/shop_products/best_selling/", {
        "shop": shop2.pk,
        "limit": 5
    })
    assert response.status_code == status.HTTP_200_OK
    products = json.loads(response.content.decode("utf-8"))
    assert len(products["results"]) == 5
    assert products["count"] == len(best_selling)
    assert products["next"] is not None
    sorted_best_selling_ids = [
        prod[0]
        for prod in sorted(best_selling.items(), key=lambda prod: -prod[1])
    ][:5]

    # check the if all the 5 best sellers are part of best selling
    for ix in range(len(products)):
        assert products["results"][ix]["product_id"] in sorted_best_selling_ids