Example #1
0
def test_create_refund_line_by_line(prices_include_tax):
    supplier = get_simple_supplier()
    order = _get_order(prices_include_tax, True, True)

    for line in order.lines.products():
        check_stock_counts(supplier, line.product, INITIAL_PRODUCT_QUANTITY, INITIAL_PRODUCT_QUANTITY - line.quantity)

    original_order_total = order.taxful_total_price
    num_order_lines = order.lines.count()

    # refund the discount lines first
    for line in order.lines.discounts():
        order.create_refund([
            {"line": line, "quantity": line.quantity, "amount": line.taxful_price.amount}])

    # refund each line 1 by 1
    for line in order.lines.products():
        order.create_refund([
            {"line": line, "quantity": line.quantity, "amount": line.taxful_price.amount, "restock_products": True}])

    for line in order.lines.products():
        check_stock_counts(supplier, line.product, INITIAL_PRODUCT_QUANTITY, INITIAL_PRODUCT_QUANTITY)

    assert order.has_refunds()
    assert not order.can_create_refund()
    assert not order.taxful_total_price_value
    assert not order.taxless_total_price_value
    assert order.lines.refunds().count() == num_order_lines
    assert order.shipping_status == ShippingStatus.FULLY_SHIPPED
    assert order.get_total_refunded_amount() == original_order_total.amount
    assert not order.get_total_unrefunded_amount().value
Example #2
0
def test_refund_entire_order():
    shop = get_default_shop()
    supplier = get_simple_supplier()
    product = create_product(
        "test-sku",
        shop=get_default_shop(),
        default_price=10,
    )
    supplier.adjust_stock(product.id, 5)
    check_stock_counts(supplier, product, 5, 5)

    order = create_order_with_product(product, supplier, 2, 200, Decimal("0.24"), shop=shop)
    order.cache_prices()

    original_total_price = order.taxful_total_price
    check_stock_counts(supplier, product, 5, 3)

    # Create a full refund with `restock_products` set to False
    order.create_full_refund(restock_products=False)

    # Confirm the refund was created with correct amount
    assert order.taxless_total_price.amount.value == 0
    assert order.taxful_total_price.amount.value == 0
    refund_line = order.lines.order_by("ordering").last()
    assert refund_line.type == OrderLineType.REFUND
    assert refund_line.taxful_price == -original_total_price

    # Make sure logical count reflects refunded products
    check_stock_counts(supplier, product, 5, 3)
Example #3
0
def test_refund_with_product_restock():
    shop = get_default_shop()
    supplier = get_simple_supplier()
    product = create_product(
        "test-sku",
        shop=get_default_shop(),
        default_price=10,
        stock_behavior=StockBehavior.STOCKED
    )
    supplier.adjust_stock(product.id, 5)
    assert supplier.get_stock_statuses([product.id])[product.id].logical_count == 5

    order = create_order_with_product(product, supplier, 2, 200, shop=shop)
    order.cache_prices()

    assert supplier.get_stock_statuses([product.id])[product.id].logical_count == 3

    # Create a refund with a parent line and quanity with `restock_products` set to False
    product_line = order.lines.first()
    order.create_refund([{"line": product_line, "quantity": 1, "restock_products": False}])

    assert supplier.get_stock_statuses([product.id])[product.id].logical_count == 3
    assert order.lines.last().taxful_price == -product_line.base_unit_price
    assert order.get_total_unrefunded_amount() == product_line.base_unit_price.amount

    # Create a refund with a parent line and quanity with `restock_products` set to True
    product_line = order.lines.first()
    order.create_refund([{"line": product_line, "quantity": 1, "restock_products": True}])

    assert supplier.get_stock_statuses([product.id])[product.id].logical_count == 4
    assert not order.taxful_total_price
Example #4
0
def test_create_refund_amount(prices_include_tax):
    supplier = get_simple_supplier()
    order = _get_order(prices_include_tax, True, True)

    original_order_total = order.taxful_total_price
    num_order_lines = order.lines.count()

    # refund the discount lines first
    for line in order.lines.discounts():
        order.create_refund([
            {"line": "amount", "quantity": line.quantity, "amount": line.taxful_price.amount}])

    # refund each line 1 by 1
    for line in order.lines.products():
        order.create_refund([
            {"line": "amount", "quantity": 1, "amount": line.taxful_price.amount, "restock_products": True}])

    assert order.has_refunds()
    #assert not order.can_create_refund()
    assert not order.taxful_total_price_value
    assert not order.taxless_total_price_value
    assert order.lines.refunds().count() == num_order_lines
    # we haven't refunded any quantity so the shipping status remains as-is
    assert order.shipping_status == ShippingStatus.NOT_SHIPPED
    assert order.payment_status == PaymentStatus.FULLY_PAID
    assert order.get_total_refunded_amount() == original_order_total.amount
    assert not order.get_total_unrefunded_amount().value

    for line in order.lines.products():
        order.create_refund([
            {"line": line, "quantity": line.quantity, "amount": Money(0, "EUR"), "restock_products": True}])

    assert order.shipping_status == ShippingStatus.FULLY_SHIPPED
Example #5
0
def test_quantity_has_to_be_in_stock(admin_user, settings):
    configure(settings)
    from shuup_tests.simple_supplier.utils import get_simple_supplier
    from shuup.core.models import StockBehavior
    shop = factories.get_default_shop()
    basket = factories.get_basket()

    supplier = get_simple_supplier()
    product = factories.create_product("simple-test-product", shop, supplier)
    quantity = 256
    supplier.adjust_stock(product.pk, quantity)
    product.stock_behavior = StockBehavior.STOCKED
    product.save()
    shop_product = product.shop_products.first()
    shop_product.suppliers.add(supplier)
    shop_product.save()

    client = _get_client(admin_user)
    payload = {
        'shop': shop.id,
        'product': shop_product.id,
        'quantity': 493020
    }
    response = client.post('/api/shuup/basket/{}-{}/add/'.format(shop.pk, basket.key), payload)
    assert response.status_code == status.HTTP_400_BAD_REQUEST
    assert '"Insufficient stock"' in str(response.content)
Example #6
0
def test_refund_entire_order():
    shop = get_default_shop()
    supplier = get_simple_supplier()
    product = create_product(
        "test-sku",
        shop=get_default_shop(),
        default_price=10,
        stock_behavior=StockBehavior.STOCKED
    )
    supplier.adjust_stock(product.id, 5)
    assert supplier.get_stock_statuses([product.id])[product.id].logical_count == 5

    order = create_order_with_product(product, supplier, 2, 200, Decimal("0.1"), shop=shop)
    order.cache_prices()
    original_total_price = order.taxful_total_price
    assert supplier.get_stock_statuses([product.id])[product.id].logical_count == 3

    # Create a full refund with `restock_products` set to False
    order.create_full_refund(restock_products=False)

    # Confirm the refund was created with correct amount
    assert order.taxless_total_price.amount.value == 0
    assert order.taxful_total_price.amount.value == 0
    refund_line = order.lines.order_by("ordering").last()
    assert refund_line.type == OrderLineType.REFUND
    assert refund_line.taxful_price == -original_total_price

    # Make sure stock status didn't change
    assert supplier.get_stock_statuses([product.id])[product.id].logical_count == 3
Example #7
0
def test_refund_without_shipment(restock):
    shop = get_default_shop()
    supplier = get_simple_supplier()
    product = create_product(
        "test-sku",
        shop=get_default_shop(),
        default_price=10,
    )
    # Start out with a supplier with quantity of 10 of a product
    supplier.adjust_stock(product.id, 10)
    check_stock_counts(supplier, product, physical=10, logical=10)

    order = create_order_with_product(product, supplier, 2, 200, shop=shop)
    order.cache_prices()
    check_stock_counts(supplier, product, physical=10, logical=8)

    # Restock value shouldn't matter if we don't have any shipments
    product_line = order.lines.first()
    order.create_refund([
        {"line": product_line, "quantity": 2, "amount": Money(400, order.currency), "restock_products": restock}])

    if restock:
        check_stock_counts(supplier, product, physical=10, logical=10)
    else:
        check_stock_counts(supplier, product, physical=10, logical=8)
    assert product_line.refunded_quantity == 2
    assert order.get_total_tax_amount() == Money(
        order.taxful_total_price_value - order.taxless_total_price_value,
        order.currency)
Example #8
0
def test_refund_entire_order_without_restock(admin_user):
    shop = get_default_shop()
    supplier = get_simple_supplier()
    product = create_product(
        "test-sku",
        shop=get_default_shop(),
        default_price=10,
    )
    supplier.adjust_stock(product.id, 5)
    _check_stock_counts(supplier, product, 5, 5)

    order = create_order_with_product(product, supplier, 2, 200, Decimal("0.24"), shop=shop)
    order.payment_status = PaymentStatus.DEFERRED
    order.cache_prices()
    order.save()

    original_total_price = order.taxful_total_price
    _check_stock_counts(supplier, product, 5, 3)

    client = _get_client(admin_user)
    refund_url = "/api/shuup/order/%s/create_full_refund/" % order.id
    data = {"restock_products": False}
    response = client.post(refund_url, data, format="json")
    assert response.status_code == status.HTTP_201_CREATED
    order.refresh_from_db()

    # Confirm the refund was created with correct amount
    assert order.taxless_total_price.amount.value == 0
    assert order.taxful_total_price.amount.value == 0
    refund_line = order.lines.order_by("ordering").last()
    assert refund_line.type == OrderLineType.REFUND
    assert refund_line.taxful_price == -original_total_price

    # Make sure logical count reflects refunded products
    _check_stock_counts(supplier, product, 5, 3)
Example #9
0
def test_supplier_with_stock_counts(rf, stock_managed):
    supplier = get_simple_supplier(stock_managed=stock_managed)
    shop = get_default_shop()
    product = create_product("simple-test-product", shop, supplier)
    quantity = random.randint(100, 600)

    if stock_managed:
        # Adjust
        supplier.adjust_stock(product.pk, quantity)
        # Check that count is adjusted
        assert supplier.get_stock_statuses([product.id])[product.id].logical_count == quantity
        # Since product is stocked with quantity we get no orderability error with quantity
        assert not list(supplier.get_orderability_errors(product.get_shop_instance(shop), quantity, customer=None))
        # Since product is stocked with quantity we get orderability error with quantity + 1
        assert list(supplier.get_orderability_errors(product.get_shop_instance(shop), quantity+1, customer=None))
    else:
        # Check that count is not adjusted
        assert supplier.get_stock_statuses([product.id])[product.id].logical_count == 0
        # No orderability errors since product is not stocked
        assert not list(supplier.get_orderability_errors(product.get_shop_instance(shop), quantity, customer=None))
        # Turn it to stocked
        supplier.stock_managed = True
        supplier.save()
        supplier.adjust_stock(product.pk, quantity)
        # Check that count is adjusted
        assert supplier.get_stock_statuses([product.id])[product.id].logical_count == quantity
        # No orderability errors since product is stocked with quantity
        assert not list(supplier.get_orderability_errors(product.get_shop_instance(shop), quantity, customer=None))
        # Since product is stocked with quantity we get orderability errors with quantity + 1
        assert list(supplier.get_orderability_errors(product.get_shop_instance(shop), quantity+1, customer=None))
Example #10
0
def test_admin_form(rf, admin_user):
    supplier = get_simple_supplier()
    shop = get_default_shop()
    product = create_product("simple-test-product", shop, supplier)
    request = rf.get("/")
    request.user = admin_user
    frm = SimpleSupplierForm(product=product, request=request)
    # Form contains 1 product even if the product is not stocked
    assert len(frm.products) == 1
    assert not frm.products[0].is_stocked()

    product.stock_behavior = StockBehavior.STOCKED  # Make product stocked
    product.save()

    # Now since product is stocked it should be in the form
    frm = SimpleSupplierForm(product=product, request=request)
    assert len(frm.products) == 1

    # Add stocked children for product
    child_product = create_product("child-test-product", shop, supplier)
    child_product.stock_behavior = StockBehavior.STOCKED
    child_product.save()
    child_product.link_to_parent(product)

    # Admin form should now contain only child products for product
    frm = SimpleSupplierForm(product=product, request=request)
    assert len(frm.products) == 1
    assert frm.products[0] == child_product
Example #11
0
def test_simple_supplier(rf):
    supplier = get_simple_supplier()
    shop = get_default_shop()
    product = create_product("simple-test-product", shop)
    ss = supplier.get_stock_status(product.pk)
    assert ss.product == product
    assert ss.logical_count == 0
    num = random.randint(100, 500)
    supplier.adjust_stock(product.pk, +num)
    assert supplier.get_stock_status(product.pk).logical_count == num
    # Create order ...
    order = create_order_with_product(product, supplier, 10, 3, shop=shop)
    quantities = order.get_product_ids_and_quantities()
    pss = supplier.get_stock_status(product.pk)
    assert pss.logical_count == (num - quantities[product.pk])
    assert pss.physical_count == num
    # Create shipment ...
    shipment = order.create_shipment_of_all_products(supplier)
    pss = supplier.get_stock_status(product.pk)
    assert pss.physical_count == (num - quantities[product.pk])
    # Cancel order...
    order.set_canceled()
    pss = supplier.get_stock_status(product.pk)
    assert pss.logical_count == (num)
    # physical stock still the same until shipment exists
    assert pss.physical_count == (num - quantities[product.pk])

    shipment.soft_delete()
    pss = supplier.get_stock_status(product.pk)
    assert pss.logical_count == num
    assert pss.physical_count == num
Example #12
0
def test_process_stock_managed(rf, admin_user):
    supplier = get_simple_supplier(stock_managed=False)
    shop = get_default_shop()
    product = create_product("simple-test-product", shop)
    request = apply_request_middleware(rf.get("/", data={"stock_managed": True}), user=admin_user)

    with pytest.raises(Exception) as ex:
        # Should raise exception becasue only POST is allowed
        response = process_stock_managed(request, supplier.id, product.id)

    request = apply_request_middleware(rf.post("/", data={"stock_managed": True}), user=admin_user)
    response = process_stock_managed(request, supplier.id, product.id)
    assert response.status_code == 200

    # Check no stock count
    sc = StockCount.objects.filter(supplier=supplier, product=product).first()
    assert sc.logical_count == 0
    # Check stock count managed by default
    assert sc.stock_managed == True
    # Now test with stock managed turned off
    request = apply_request_middleware(rf.post("/", data={"stock_managed": False}), user=admin_user)
    response = process_stock_managed(request, supplier.id, product.id)
    assert response.status_code == 200
    # Check stock management is disabled for product
    sc = StockCount.objects.filter(
        supplier=supplier, product=product).first()
    assert sc.stock_managed == False
    # Now test with stock managed turned on
    request = apply_request_middleware(rf.post("/", data={"stock_managed": True}), user=admin_user)
    response = process_stock_managed(request, supplier.id, product.id)
    assert response.status_code == 200
    # Check stock management is enabled for product
    sc = StockCount.objects.filter(
        supplier=supplier, product=product).first()
    assert sc.stock_managed == True
Example #13
0
def test_shipment_with_insufficient_stock():
    if "shuup.simple_supplier" not in settings.INSTALLED_APPS:
        pytest.skip("Need shuup.simple_supplier in INSTALLED_APPS")

    from shuup_tests.simple_supplier.utils import get_simple_supplier

    shop = get_default_shop()
    supplier = get_simple_supplier()
    order = _get_order(shop, supplier, stocked=True)
    product_line = order.lines.products().first()
    product = product_line.product
    assert product_line.quantity == 15

    supplier.adjust_stock(product.pk, delta=10)
    stock_status = supplier.get_stock_status(product.pk)
    assert stock_status.physical_count == 10

    order.create_shipment({product: 5}, supplier=supplier)
    assert order.shipping_status == ShippingStatus.PARTIALLY_SHIPPED
    assert order.shipments.all().count() == 1

    with pytest.raises(Problem):
        order.create_shipment({product: 10}, supplier=supplier)

    # Should be fine after adding more stock
    supplier.adjust_stock(product.pk, delta=5)
    order.create_shipment({product: 10}, supplier=supplier)
def test_simple_supplier(rf):
    supplier = get_simple_supplier()
    shop = get_default_shop()
    product = create_product("simple-test-product", shop)
    ss = supplier.get_stock_status(product.pk)
    assert ss.product == product
    assert ss.logical_count == 0
    num = random.randint(100, 500)
    supplier.adjust_stock(product.pk, +num)
    assert supplier.get_stock_status(product.pk).logical_count == num

    # Create order
    order = create_order_with_product(product, supplier, 10, 3, shop=shop)
    quantities = order.get_product_ids_and_quantities()
    pss = supplier.get_stock_status(product.pk)
    assert pss.logical_count == (num - quantities[product.pk])
    assert pss.physical_count == num
    # Create shipment

    shipment = order.create_shipment_of_all_products(supplier)
    assert isinstance(shipment, Shipment)
    pss = supplier.get_stock_status(product.pk)
    assert pss.logical_count == (num - quantities[product.pk])
    assert pss.physical_count == (num - quantities[product.pk])

    # Delete shipment
    with pytest.raises(NotImplementedError):
        shipment.delete()
    shipment.soft_delete()
    assert shipment.is_deleted()
    pss = supplier.get_stock_status(product.pk)
    assert pss.logical_count == (num - quantities[product.pk])
    assert pss.physical_count == (num)
Example #15
0
def test_order_source(rf, admin_user):
    """
    Test order source validation with stocked products.
    """
    shop = get_default_shop()
    supplier = get_simple_supplier()
    product = create_product("simple-test-product", shop, supplier)
    product.stock_behavior = StockBehavior.STOCKED
    product.save()
    quantity = 345
    supplier.adjust_stock(product.pk, quantity)
    assert supplier.get_stock_statuses([product.id])[product.id].logical_count == quantity
    assert not list(supplier.get_orderability_errors(product.get_shop_instance(shop), quantity, customer=None))
    assert list(supplier.get_orderability_errors(product.get_shop_instance(shop), quantity+1, customer=None))

    source = seed_source(admin_user, shop)
    source.add_line(
        type=OrderLineType.PRODUCT,
        product=product,
        supplier=supplier,
        quantity=quantity,
        base_unit_price=source.create_price(10),
    )
    assert not list(source.get_validation_errors())

    source.add_line(
        type=OrderLineType.PRODUCT,
        product=product,
        supplier=supplier,
        quantity=quantity,
        base_unit_price=source.create_price(10),
    )
    assert list(source.get_validation_errors())
Example #16
0
def test_refund_entire_order_with_restock(admin_user):
    shop = get_default_shop()
    supplier = get_simple_supplier()
    product = create_product(
        "test-sku",
        shop=get_default_shop(),
        default_price=10,
    )
    supplier.adjust_stock(product.id, 5)
    _check_stock_counts(supplier, product, 5, 5)

    order = create_order_with_product(product, supplier, 2, 200, shop=shop)
    order.payment_status = PaymentStatus.DEFERRED
    order.cache_prices()
    order.save()

    _check_stock_counts(supplier, product, 5, 3)
    assert not StockAdjustment.objects.filter(created_by=admin_user).exists()

    client = _get_client(admin_user)
    refund_url = "/api/shuup/order/%s/create_full_refund/" % order.id
    data = {"restock_products": True}
    response = client.post(refund_url, data, format="json")
    assert response.status_code == status.HTTP_201_CREATED
    order.refresh_from_db()

    # restock logical count
    _check_stock_counts(supplier, product, 5, 5)
    assert StockAdjustment.objects.filter(created_by=admin_user).exists()
def test_get_listed_products_orderable_only():
    context = get_jinja_context()
    shop = get_default_shop()
    simple_supplier = get_simple_supplier()
    n_products = 2

    # Create product without stock
    product = create_product(
        "test-sku",
        supplier=simple_supplier,
        shop=shop,
        stock_behavior=StockBehavior.STOCKED
    )
    assert len(general.get_listed_products(context, n_products, orderable_only=True)) == 0
    assert len(general.get_listed_products(context, n_products, orderable_only=False)) == 1

    # Increase stock on product
    quantity = product.get_shop_instance(shop).minimum_purchase_quantity
    simple_supplier.adjust_stock(product.id, quantity)
    assert len(general.get_listed_products(context, n_products, orderable_only=True)) == 1
    assert len(general.get_listed_products(context, n_products, orderable_only=False)) == 1

    # Decrease stock on product
    simple_supplier.adjust_stock(product.id, -quantity)
    assert len(general.get_listed_products(context, n_products, orderable_only=True)) == 0
    assert len(general.get_listed_products(context, n_products, orderable_only=False)) == 1
Example #18
0
def test_sample_import_no_match(stock_managed):
    filename = "sample_import_nomatch.xlsx"
    if "shuup.simple_supplier" not in settings.INSTALLED_APPS:
        pytest.skip("Need shuup.simple_supplier in INSTALLED_APPS")
    from shuup_tests.simple_supplier.utils import get_simple_supplier

    activate("en")
    shop = get_default_shop()
    tax_class = get_default_tax_class()
    product_type = get_default_product_type()
    supplier = get_simple_supplier(stock_managed)
    sales_unit = get_default_sales_unit()

    Manufacturer.objects.create(name="manufctr")
    path = os.path.join(os.path.dirname(__file__), "data", "product", filename)
    transformed_data = transform_file(filename.split(".")[1], path)

    importer = ProductImporter(transformed_data, shop, "en")
    importer.process_data()
    assert len(importer.unmatched_fields) == 1
    assert "gtiin" in importer.unmatched_fields
    importer.manually_match("gtiin", "shuup.core.models.Product:gtin")
    importer.do_remap()
    assert len(importer.unmatched_fields) == 0
    importer.do_import(ImportMode.CREATE_UPDATE)
    products = importer.new_objects
    assert len(products) == 2

    for product in products:
        assert product.gtin == "1280x720"
        shop_product = product.get_shop_instance(shop)
        assert shop_product.pk
        assert shop_product.default_price_value == 150
        assert shop_product.default_price == shop.create_price(150)
        assert product.type == product_type  # product type comes from importer defaults
        assert product.sales_unit == sales_unit
        if product.pk == 1:
            assert product.tax_class.pk == 2  # new was created
            assert product.name == "Product English"
            assert product.description == "Description English"
        else:
            assert product.tax_class.pk == tax_class.pk  # old was found as should
            assert product.name == "Product 2 English"
            assert product.description == "Description English 2"
        assert shop_product.primary_category.pk == 1
        assert [c.pk for c in shop_product.categories.all()] == [1,2]

    # stock was not managed since supplier doesn't like that
    for msg in importer.other_log_messages:
        assert "please set Stock Managed on" in msg

    supplier.stock_managed = True
    supplier.save()
    importer.do_import("create,update")

    assert len(importer.other_log_messages) == 0
    for sa in StockAdjustment.objects.all():
        assert sa.product.pk
        assert sa.delta == 20
Example #19
0
def test_order_creator_with_package_product(rf, admin_user):
    if "shuup.simple_supplier" not in settings.INSTALLED_APPS:
        pytest.skip("Need shuup.simple_supplier in INSTALLED_APPS")
    from shuup_tests.simple_supplier.utils import get_simple_supplier

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

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

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

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

    creator = OrderCreator()

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

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

    # And a validation error should be raised
    with pytest.raises(ValidationError):
        order = creator.create_order(source)
Example #20
0
def test_basket_package_product_orderability_change(rf):
    if "shuup.simple_supplier" not in settings.INSTALLED_APPS:
        pytest.skip("Need shuup.simple_supplier in INSTALLED_APPS")
    from shuup_tests.simple_supplier.utils import get_simple_supplier

    StoredBasket.objects.all().delete()
    shop = get_default_shop()
    supplier = get_simple_supplier()
    product, child = get_unstocked_package_product_and_stocked_child(shop, supplier, child_logical_quantity=2)
    request = rf.get("/")
    request.session = {}
    request.shop = shop
    apply_request_middleware(request)
    basket = get_basket(request)

    # Add the package parent
    basket.add_product(
        supplier=supplier,
        shop=shop,
        product=product,
        quantity=1,
        force_new_line=True,
        extra={"foo": "foo"}
    )

    # Also add the child product separately
    basket.add_product(
        supplier=supplier,
        shop=shop,
        product=child,
        quantity=1,
        force_new_line=True,
        extra={"foo": "foo"}
    )

    # Should be stock for both
    assert len(basket.get_lines()) == 2
    assert len(basket.get_unorderable_lines()) == 0

    supplier.adjust_stock(child.id, -1)

    # Orderability is already cached, we need to uncache to force recheck
    basket.uncache()

    # After reducing stock to 1, should only be stock for one
    assert len(basket.get_lines()) == 1
    assert len(basket.get_unorderable_lines()) == 1

    supplier.adjust_stock(child.id, -1)

    basket.uncache()

    # After reducing stock to 0, should be stock for neither
    assert len(basket.get_lines()) == 0
    assert len(basket.get_unorderable_lines()) == 2
Example #21
0
def test_refund_with_shipment(restock):
    shop = get_default_shop()
    supplier = get_simple_supplier()
    product = create_product(
        "test-sku",
        shop=get_default_shop(),
        default_price=10,
    )
    # Start out with a supplier with quantity of 10 of a product
    supplier.adjust_stock(product.id, 10)
    check_stock_counts(supplier, product, physical=10, logical=10)

    # Order 4 products, make sure product counts are accurate
    order = create_order_with_product(product, supplier, 4, 200, shop=shop)
    order.cache_prices()
    check_stock_counts(supplier, product, physical=10, logical=6)
    product_line = order.lines.first()

    # Shipment should decrease physical count by 2, logical by none
    order.create_shipment({product_line.product: 2}, supplier=supplier)
    check_stock_counts(supplier, product, physical=8, logical=6)
    assert order.shipping_status == ShippingStatus.PARTIALLY_SHIPPED

    # Check correct refunded quantities
    assert not product_line.refunded_quantity

    # Create a refund that refunds from unshipped quantity first, then shipped quantity, check stocks
    check_stock_counts(supplier, product, physical=8, logical=6)
    order.create_refund([
        {"line": product_line, "quantity": 3, "amount": Money(600, order.currency), "restock_products": restock}])
    assert product_line.refunded_quantity == 3
    assert order.shipping_status == ShippingStatus.FULLY_SHIPPED
    if restock:
        check_stock_counts(supplier, product, physical=9, logical=9)
    else:
        check_stock_counts(supplier, product, physical=8, logical=6)

    # Create a second refund that refunds the last shipped quantity, check stocks
    order.create_refund([
        {"line": product_line, "quantity": 1, "amount": Money(200, order.currency), "restock_products": restock}])
    assert product_line.refunded_quantity == 4
    if restock:
        # Make sure we're not restocking more than maximum restockable quantity
        check_stock_counts(supplier, product, physical=10, logical=10)
    else:
        # Make sure maximum restockable quantity is not 0
        check_stock_counts(supplier, product, physical=8, logical=6)
    assert order.get_total_tax_amount() == Money(
        order.taxful_total_price_value - order.taxless_total_price_value,
        order.currency)
Example #22
0
def test_basket_update_with_package_product():
    if "shuup.simple_supplier" not in settings.INSTALLED_APPS:
        pytest.skip("Need shuup.simple_supplier in INSTALLED_APPS")
    from shuup_tests.simple_supplier.utils import get_simple_supplier

    request = get_request_with_basket()
    basket = request.basket
    shop = get_default_shop()
    supplier = get_simple_supplier()
    parent, child = get_unstocked_package_product_and_stocked_child(shop, supplier, child_logical_quantity=2)

    # There should be enough stock for 1 parent and 1 extra child, each of quantity 1
    basket_commands.handle_add(request, basket, product_id=parent.pk, quantity=1)
    assert basket.product_count == 1
    basket_commands.handle_add(request, basket, product_id=child.pk, quantity=1)
    assert basket.product_count == 2
    assert not messages.get_messages(request)

    basket_lines = {line.product.id: line for line in basket.get_lines()}
    package_line = basket_lines[parent.id]
    extra_child_line = basket_lines[child.id]

    # Trying to increase package product line quantity should fail, with error message
    basket_commands.handle_update(request, basket, **{"q_%s" % package_line.line_id: "2"})
    assert basket.product_count == 2
    assert len(messages.get_messages(request)) == 1

    # So should increasing the extra child line quantity
    basket_commands.handle_update(request, basket, **{"q_%s" % extra_child_line.line_id: "2"})
    assert basket.product_count == 2
    assert len(messages.get_messages(request)) == 2

    # However, if we delete the parent line, we can increase the extra child
    basket_commands.handle_update(request, basket, **{"delete_%s" % package_line.line_id: "1"})
    assert basket.product_count == 1
    basket_commands.handle_update(request, basket, **{"q_%s" % extra_child_line.line_id: "2"})
    assert basket.product_count == 2

    # Resetting to original basket contents
    basket_commands.handle_update(request, basket, **{"q_%s" % extra_child_line.line_id: "1"})
    basket_commands.handle_add(request, basket, product_id=parent.pk, quantity=1)
    basket_lines = {line.product.id: line for line in basket.get_lines()}
    package_line = basket_lines[parent.id]  # Package line will have a new ID
    assert basket.product_count == 2

    # Like above, delete the child line and we can now increase the parent
    basket_commands.handle_update(request, basket, **{"delete_%s" % extra_child_line.line_id: "1"})
    assert basket.product_count == 1
    basket_commands.handle_update(request, basket, **{"q_%s" % package_line.line_id: "2"})
    assert basket.product_count == 2
Example #23
0
def test_supplier_with_stock_counts(rf):
    supplier = get_simple_supplier()
    product = create_product("simple-test-product", shop, supplier)
    quantity = random.randint(100, 600)
    supplier.adjust_stock(product.pk, quantity)
    assert supplier.get_stock_statuses([product.id])[product.id].logical_count == quantity
    # No orderability errors since product is not stocked
    assert not list(supplier.get_orderability_errors(product.get_shop_instance(shop), quantity+1, customer=None))

    product.stock_behavior = StockBehavior.STOCKED  # Make product stocked
    product.save()

    assert not list(supplier.get_orderability_errors(product.get_shop_instance(shop), quantity, customer=None))
    # Now since product is stocked we get orderability error with quantity + 1
    assert list(supplier.get_orderability_errors(product.get_shop_instance(shop), quantity+1, customer=None))
def _get_custom_order(regular_user, **kwargs):
    prices_include_tax = kwargs.pop("prices_include_tax", False)
    include_basket_campaign = kwargs.pop("include_basket_campaign", False)
    include_catalog_campaign = kwargs.pop("include_catalog_campaign", False)

    shop = get_shop(prices_include_tax=prices_include_tax)
    supplier = get_simple_supplier()

    if include_basket_campaign:
        _add_basket_campaign(shop)

    if include_catalog_campaign:
        _add_catalog_campaign(shop)
    _add_taxes()

    contact = get_person_contact(regular_user)
    source = BasketishOrderSource(shop)
    source.status = get_initial_order_status()
    source.customer = contact

    ctx = get_pricing_module().get_context_from_data(shop, contact)
    for product_data in _get_product_data():
        quantity = product_data.pop("quantity")
        product = create_product(
            sku=product_data.pop("sku"),
            shop=shop,
            supplier=supplier,
            stock_behavior=StockBehavior.STOCKED,
            tax_class=get_default_tax_class(),
            **product_data)
        shop_product = product.get_shop_instance(shop)
        shop_product.categories.add(get_default_category())
        shop_product.save()
        supplier.adjust_stock(product.id, INITIAL_PRODUCT_QUANTITY)
        pi = product.get_price_info(ctx)
        source.add_line(
            type=OrderLineType.PRODUCT,
            product=product,
            supplier=supplier,
            quantity=quantity,
            base_unit_price=pi.base_unit_price,
            discount_amount=pi.discount_amount
        )

    oc = OrderCreator()
    order = oc.create_order(source)
    return order
def _get_custom_order(regular_user, **kwargs):
    prices_include_tax = kwargs.pop("prices_include_tax", False)
    include_basket_campaign = kwargs.pop("include_basket_campaign", False)
    include_catalog_campaign = kwargs.pop("include_catalog_campaign", False)

    shop = get_shop(prices_include_tax=prices_include_tax)
    supplier = get_simple_supplier()

    if include_basket_campaign:
        _add_basket_campaign(shop)

    if include_catalog_campaign:
        _add_catalog_campaign(shop)
    _add_taxes()

    contact = get_person_contact(regular_user)
    source = BasketishOrderSource(shop)
    source.status = get_initial_order_status()
    source.customer = contact

    ctx = get_pricing_module().get_context_from_data(shop, contact)
    for product_data in _get_product_data():
        quantity = product_data.pop("quantity")
        product = create_product(
            sku=product_data.pop("sku"),
            shop=shop,
            supplier=supplier,
            tax_class=get_default_tax_class(),
            **product_data)
        shop_product = product.get_shop_instance(shop)
        shop_product.categories.add(get_default_category())
        shop_product.save()
        supplier.adjust_stock(product.id, INITIAL_PRODUCT_QUANTITY)
        pi = product.get_price_info(ctx)
        source.add_line(
            type=OrderLineType.PRODUCT,
            product=product,
            supplier=supplier,
            quantity=quantity,
            base_unit_price=pi.base_unit_price,
            discount_amount=pi.discount_amount
        )

    oc = OrderCreator()
    order = oc.create_order(source)
    return order
Example #26
0
def test_refund_with_shipment(restock):
    shop = get_default_shop()
    supplier = get_simple_supplier()
    product = create_product(
        "test-sku",
        shop=get_default_shop(),
        default_price=10,
        stock_behavior=StockBehavior.STOCKED
    )
    # Start out with a supplier with quantity of 10 of a product
    supplier.adjust_stock(product.id, 10)
    check_stock_counts(supplier, product, physical=10, logical=10)

    # Order 4 products, make sure product counts are accurate
    order = create_order_with_product(product, supplier, 4, 200, shop=shop)
    order.cache_prices()
    check_stock_counts(supplier, product, physical=10, logical=6)
    product_line = order.lines.first()

    # Shipment should decrease physical count by 2, logical by none
    order.create_shipment({product_line.product: 2}, supplier=supplier)
    check_stock_counts(supplier, product, physical=8, logical=6)
    assert order.shipping_status ==  ShippingStatus.PARTIALLY_SHIPPED

    # Check correct refunded quantities
    assert not product_line.refunded_quantity

    # Create a refund greater than restockable quantity, check stocks
    check_stock_counts(supplier, product, physical=8, logical=6)
    order.create_refund([{"line": product_line, "quantity": 3, "restock_products": restock}])
    assert product_line.refunded_quantity == 3
    assert order.shipping_status == ShippingStatus.FULLY_SHIPPED
    if restock:
        check_stock_counts(supplier, product, physical=10, logical=8)
    else:
        check_stock_counts(supplier, product, physical=8, logical=6)

    # Create a second refund greater than restockable quantity, check stocks
    order.create_refund([{"line": product_line, "quantity": 1, "restock_products": restock}])
    assert product_line.refunded_quantity == 4
    if restock:
        # Make sure we're not restocking more than maximum restockable quantity
        check_stock_counts(supplier, product, physical=10, logical=8)
    else:
        # Make sure maximum restockable quantity is not 0
        check_stock_counts(supplier, product, physical=8, logical=6)
Example #27
0
def test_partial_refund_limits(restock):
    shop = get_default_shop()
    supplier = get_simple_supplier()
    product = create_product(
        "test-sku",
        shop=get_default_shop(),
        default_price=10,
    )
    # Start out with a supplier with quantity of 10 of a product
    supplier.adjust_stock(product.id, 10)
    check_stock_counts(supplier, product, physical=10, logical=10)

    quantity = 2
    order = create_order_with_product(product,
                                      supplier,
                                      quantity,
                                      200,
                                      shop=shop)
    order.cache_prices()
    check_stock_counts(supplier, product, physical=10, logical=8)

    # try creating more partial refunds than possible
    product_line = order.lines.first()

    def create_refund():
        order.create_refund([{
            "line": product_line,
            "quantity": 1,
            "amount": Money(1, order.currency),
            "restock_products": restock
        }])

    # create more refunds than available
    for index in range(quantity + 1):
        if index == quantity:
            with pytest.raises(RefundExceedsQuantityException):
                create_refund()
        else:
            create_refund()

    if restock:
        check_stock_counts(supplier, product, physical=10, logical=10)
    else:
        check_stock_counts(supplier, product, physical=10, logical=8)

    assert product_line.refunded_quantity == 2
Example #28
0
def test_get_listed_products_orderable_only(reindex_catalog):
    context = get_jinja_context()
    shop = get_default_shop()
    simple_supplier = get_simple_supplier()
    n_products = 2

    # Create product without stock
    product = create_product("test-sku", supplier=simple_supplier, shop=shop)

    create_product("test-sku-2", supplier=simple_supplier, shop=shop)
    create_product("test-sku-3", supplier=simple_supplier, shop=shop)
    create_product("test-sku-4", supplier=simple_supplier, shop=shop)

    reindex_catalog()

    from shuup.front.template_helpers import general

    assert len(
        general.get_listed_products(context, n_products,
                                    orderable_only=True)) == 0
    assert len(
        general.get_listed_products(context, n_products,
                                    orderable_only=False)) == 2

    # Increase stock on product
    quantity = product.get_shop_instance(shop).minimum_purchase_quantity
    simple_supplier.adjust_stock(product.id, quantity)
    reindex_catalog()

    assert len(
        general.get_listed_products(context, n_products,
                                    orderable_only=True)) == 1
    assert len(
        general.get_listed_products(context, n_products,
                                    orderable_only=False)) == 2

    # Decrease stock on product
    simple_supplier.adjust_stock(product.id, -quantity)
    reindex_catalog()

    assert len(
        general.get_listed_products(context, n_products,
                                    orderable_only=True)) == 0
    assert len(
        general.get_listed_products(context, n_products,
                                    orderable_only=False)) == 2
Example #29
0
def _get_order(prices_include_tax=False,
               include_basket_campaign=False,
               include_catalog_campaign=False):
    shop = get_shop(prices_include_tax=prices_include_tax)
    supplier = get_simple_supplier()

    if "shuup.campaigns" in settings.INSTALLED_APPS:
        from shuup.campaigns.models.basket_conditions import BasketTotalProductAmountCondition
        from shuup.campaigns.models.basket_effects import BasketDiscountAmount
        from shuup.campaigns.models.campaigns import BasketCampaign, CatalogCampaign
        from shuup.campaigns.models.catalog_filters import CategoryFilter
        from shuup.campaigns.models.product_effects import ProductDiscountAmount

        if include_basket_campaign:
            _add_basket_campaign(shop)

        if include_catalog_campaign:
            _add_catalog_campaign(shop)
    _add_taxes()

    source = BasketishOrderSource(shop)
    source.status = get_initial_order_status()
    ctx = get_pricing_module().get_context_from_data(shop, AnonymousContact())
    for product_data in _get_product_data():
        quantity = product_data.pop("quantity")
        product = create_product(sku=product_data.pop("sku"),
                                 shop=shop,
                                 supplier=supplier,
                                 stock_behavior=StockBehavior.STOCKED,
                                 tax_class=get_default_tax_class(),
                                 **product_data)
        shop_product = product.get_shop_instance(shop)
        shop_product.categories.add(get_default_category())
        shop_product.save()
        supplier.adjust_stock(product.id, INITIAL_PRODUCT_QUANTITY)
        pi = product.get_price_info(ctx)
        source.add_line(type=OrderLineType.PRODUCT,
                        product=product,
                        supplier=supplier,
                        quantity=quantity,
                        base_unit_price=pi.base_unit_price,
                        discount_amount=pi.discount_amount)
    oc = OrderCreator()
    order = oc.create_order(source)
    return order
Example #30
0
def test_create_refund_line_by_line(prices_include_tax):

    if "shuup.simple_supplier" not in settings.INSTALLED_APPS:
        pytest.skip("Need shuup.simple_supplier in INSTALLED_APPS")
    from shuup_tests.simple_supplier.utils import get_simple_supplier

    supplier = get_simple_supplier()
    order = _get_order(prices_include_tax, True, True)

    for line in order.lines.products():
        check_stock_counts(supplier, line.product, INITIAL_PRODUCT_QUANTITY,
                           INITIAL_PRODUCT_QUANTITY - line.quantity)

    original_order_total = order.taxful_total_price
    num_order_lines = order.lines.count()

    # refund the discount lines first
    for line in order.lines.discounts():
        order.create_refund([{
            "line": line,
            "quantity": line.quantity,
            "amount": line.taxful_price.amount
        }])

    # refund each line 1 by 1
    for line in order.lines.products():
        order.create_refund([{
            "line": line,
            "quantity": line.quantity,
            "amount": line.taxful_price.amount,
            "restock_products": True
        }])

    for line in order.lines.products():
        check_stock_counts(supplier, line.product, INITIAL_PRODUCT_QUANTITY,
                           INITIAL_PRODUCT_QUANTITY)

    assert order.has_refunds()
    assert not order.can_create_refund()
    assert not order.taxful_total_price_value
    assert not order.taxless_total_price_value
    assert order.lines.refunds().count() == num_order_lines
    assert order.shipping_status == ShippingStatus.FULLY_SHIPPED
    assert order.get_total_refunded_amount() == original_order_total.amount
    assert not order.get_total_unrefunded_amount().value
Example #31
0
def test_supplier_with_stock_counts(rf, stock_managed):
    supplier = get_simple_supplier(stock_managed=stock_managed)
    shop = get_default_shop()
    product = create_product("simple-test-product", shop, supplier)
    quantity = random.randint(100, 600)

    if stock_managed:
        # Adjust
        supplier.adjust_stock(product.pk, quantity)
        # Check that count is adjusted
        assert supplier.get_stock_statuses(
            [product.id])[product.id].logical_count == quantity
        # Since product is stocked with quantity we get no orderability error with quantity
        assert not list(
            supplier.get_orderability_errors(
                product.get_shop_instance(shop), quantity, customer=None))
        # Since product is stocked with quantity we get orderability error with quantity + 1
        assert list(
            supplier.get_orderability_errors(product.get_shop_instance(shop),
                                             quantity + 1,
                                             customer=None))
    else:
        # Check that count is not adjusted
        assert supplier.get_stock_statuses([product.id
                                            ])[product.id].logical_count == 0
        # No orderability errors since product is not stocked
        assert not list(
            supplier.get_orderability_errors(
                product.get_shop_instance(shop), quantity, customer=None))
        # Turn it to stocked
        supplier.stock_managed = True
        supplier.save()
        supplier.adjust_stock(product.pk, quantity)
        # Check that count is adjusted
        assert supplier.get_stock_statuses(
            [product.id])[product.id].logical_count == quantity
        # No orderability errors since product is stocked with quantity
        assert not list(
            supplier.get_orderability_errors(
                product.get_shop_instance(shop), quantity, customer=None))
        # Since product is stocked with quantity we get orderability errors with quantity + 1
        assert list(
            supplier.get_orderability_errors(product.get_shop_instance(shop),
                                             quantity + 1,
                                             customer=None))
Example #32
0
def test_alert_limit_view(rf, admin_user):
    supplier = get_simple_supplier()
    shop = get_default_shop()
    product = create_product("simple-test-product", shop, supplier)
    sc = StockCount.objects.get(supplier=supplier, product=product)
    assert not sc.alert_limit

    test_alert_limit = decimal.Decimal(10)
    request = apply_request_middleware(rf.get("/"), user=admin_user)
    request.method = "POST"
    request.POST = {
        "alert_limit": test_alert_limit,
    }
    response = process_alert_limit(request, supplier.id, product.id)
    assert response.status_code == 200

    sc = StockCount.objects.get(supplier=supplier, product=product)
    assert sc.alert_limit == test_alert_limit
Example #33
0
def test_alert_limit_view(rf, admin_user):
    supplier = get_simple_supplier()
    shop = get_default_shop()
    product = create_product("simple-test-product", shop, supplier)
    sc = StockCount.objects.get(supplier=supplier, product=product)
    assert not sc.alert_limit

    test_alert_limit = decimal.Decimal(10)
    request = apply_request_middleware(rf.get("/"), user=admin_user)
    request.method = "POST"
    request.POST = {
        "alert_limit": test_alert_limit,
    }
    response = process_alert_limit(request, supplier.id, product.id)
    assert response.status_code == 200

    sc = StockCount.objects.get(supplier=supplier, product=product)
    assert sc.alert_limit == test_alert_limit
Example #34
0
def _get_order(prices_include_tax=False, include_basket_campaign=False, include_catalog_campaign=False):
    shop = get_shop(prices_include_tax=prices_include_tax)
    supplier = get_simple_supplier()

    if include_basket_campaign:
        _add_basket_campaign(shop)

    if include_catalog_campaign:
        _add_catalog_campaign(shop)
    _add_taxes()

    source = BasketishOrderSource(shop)
    source.status = get_initial_order_status()
    ctx = get_pricing_module().get_context_from_data(shop, AnonymousContact())
    for product_data in _get_product_data():
        quantity = product_data.pop("quantity")
        product = create_product(
            sku=product_data.pop("sku"),
            shop=shop,
            supplier=supplier,
            stock_behavior=StockBehavior.STOCKED,
            tax_class=get_default_tax_class(),
            **product_data)
        shop_product = product.get_shop_instance(shop)
        shop_product.categories.add(get_default_category())
        shop_product.save()
        supplier.adjust_stock(product.id, INITIAL_PRODUCT_QUANTITY)
        pi = product.get_price_info(ctx)
        source.add_line(
            type=OrderLineType.PRODUCT,
            product=product,
            supplier=supplier,
            quantity=quantity,
            base_unit_price=pi.base_unit_price,
            discount_amount=pi.discount_amount
        )
    oc = OrderCreator()
    order = oc.create_order(source)
    order.create_payment(Money("1", "EUR"))
    assert not order.has_refunds()
    assert order.can_create_refund()
    assert order.shipping_status == ShippingStatus.NOT_SHIPPED
    assert order.payment_status == PaymentStatus.PARTIALLY_PAID
    return order
Example #35
0
def test_supplier_with_stock_counts_2(rf, admin_user, settings):
    with override_settings(SHUUP_HOME_CURRENCY="USD",
                           SHUUP_ENABLE_MULTIPLE_SHOPS=False):
        supplier = get_simple_supplier()
        shop = get_default_shop()
        assert shop.prices_include_tax
        assert shop.currency != settings.SHUUP_HOME_CURRENCY
        product = create_product("simple-test-product", shop, supplier)
        quantity = random.randint(100, 600)
        supplier.adjust_stock(product.pk, quantity)
        adjust_quantity = random.randint(100, 600)
        request = apply_request_middleware(rf.get("/"), user=admin_user)
        request.POST = {
            "purchase_price": decimal.Decimal(32.00),
            "delta": adjust_quantity
        }
        response = process_stock_adjustment(request, supplier.id, product.id)
        assert response.status_code == 405  # Only POST is allowed
        request.method = "POST"
        response = process_stock_adjustment(request, supplier.id, product.id)
        assert response.status_code == 200
        pss = supplier.get_stock_status(product.pk)
        # Product stock values should be adjusted
        assert pss.logical_count == (quantity + adjust_quantity)
        # test price properties
        sa = StockAdjustment.objects.first()
        assert sa.purchase_price.currency == shop.currency
        assert sa.purchase_price.includes_tax
        sc = StockCount.objects.first()
        assert sc.stock_value.currency == shop.currency
        assert sc.stock_value.includes_tax
        assert sc.stock_unit_price.currency == shop.currency
        assert sc.stock_unit_price.includes_tax

        with override_settings(SHUUP_ENABLE_MULTIPLE_SHOPS=True):
            sa = StockAdjustment.objects.first()  # refetch to invalidate cache
            assert sa.purchase_price.currency != shop.currency
            assert sa.purchase_price.currency == settings.SHUUP_HOME_CURRENCY
            assert not sa.purchase_price.includes_tax
            sc = StockCount.objects.first()
            assert sc.stock_value.currency == settings.SHUUP_HOME_CURRENCY
            assert not sc.stock_value.includes_tax
            assert sc.stock_unit_price.currency == settings.SHUUP_HOME_CURRENCY
            assert not sc.stock_unit_price.includes_tax
Example #36
0
def _get_order(prices_include_tax=False, include_basket_campaign=False, include_catalog_campaign=False):
    shop = get_shop(prices_include_tax=prices_include_tax)
    supplier = get_simple_supplier()

    if include_basket_campaign:
        _add_basket_campaign(shop)

    if include_catalog_campaign:
        _add_catalog_campaign(shop)
    _add_taxes()

    source = BasketishOrderSource(shop)
    source.status = get_initial_order_status()
    ctx = get_pricing_module().get_context_from_data(shop, AnonymousContact())
    for product_data in _get_product_data():
        quantity = product_data.pop("quantity")
        product = create_product(
            sku=product_data.pop("sku"),
            shop=shop,
            supplier=supplier,
            stock_behavior=StockBehavior.STOCKED,
            tax_class=get_default_tax_class(),
            **product_data)
        shop_product = product.get_shop_instance(shop)
        shop_product.categories.add(get_default_category())
        shop_product.save()
        supplier.adjust_stock(product.id, INITIAL_PRODUCT_QUANTITY)
        pi = product.get_price_info(ctx)
        source.add_line(
            type=OrderLineType.PRODUCT,
            product=product,
            supplier=supplier,
            quantity=quantity,
            base_unit_price=pi.base_unit_price,
            discount_amount=pi.discount_amount
        )
    oc = OrderCreator()
    order = oc.create_order(source)
    order.create_payment(Money("1", "EUR"))
    assert not order.has_refunds()
    assert order.can_create_refund()
    assert order.shipping_status == ShippingStatus.NOT_SHIPPED
    assert order.payment_status == PaymentStatus.PARTIALLY_PAID
    return order
Example #37
0
def test_create_refund_amount(prices_include_tax):
    supplier = get_simple_supplier()
    order = _get_order(prices_include_tax, True, True)

    original_order_total = order.taxful_total_price
    num_order_lines = order.lines.count()

    # refund the discount lines first
    for line in order.lines.discounts():
        order.create_refund([{
            "line": "amount",
            "quantity": line.quantity,
            "amount": line.taxful_price.amount
        }])

    # refund each line 1 by 1
    for line in order.lines.products():
        order.create_refund([{
            "line": "amount",
            "quantity": 1,
            "amount": line.taxful_price.amount,
            "restock_products": True
        }])

    assert order.has_refunds()
    #assert not order.can_create_refund()
    assert not order.taxful_total_price_value
    assert not order.taxless_total_price_value
    assert order.lines.refunds().count() == num_order_lines
    # we haven't refunded any quantity so the shipping status remains as-is
    assert order.shipping_status == ShippingStatus.NOT_SHIPPED
    assert order.payment_status == PaymentStatus.FULLY_PAID
    assert order.get_total_refunded_amount() == original_order_total.amount
    assert not order.get_total_unrefunded_amount().value

    for line in order.lines.products():
        order.create_refund([{
            "line": line,
            "quantity": line.quantity,
            "amount": Money(0, "EUR"),
            "restock_products": True
        }])

    assert order.shipping_status == ShippingStatus.FULLY_SHIPPED
Example #38
0
def test_supplier_with_stock_counts(rf, admin_user, settings):
    supplier = get_simple_supplier()
    shop = get_default_shop()
    settings.SHUUP_HOME_CURRENCY = "USD"
    assert shop.prices_include_tax
    assert shop.currency != settings.SHUUP_HOME_CURRENCY
    product = create_product("simple-test-product", shop, supplier)
    quantity = random.randint(100, 600)
    supplier.adjust_stock(product.pk, quantity)
    adjust_quantity = random.randint(100, 600)
    request = rf.get("/")
    request.user = admin_user
    request.POST = {
        "purchase_price": decimal.Decimal(32.00),
        "delta": adjust_quantity
    }
    response = process_stock_adjustment(request, supplier.id, product.id)
    assert response.status_code == 400  # Only POST is allowed
    request.method = "POST"
    response = process_stock_adjustment(request, supplier.id, product.id)
    assert response.status_code == 200
    pss = supplier.get_stock_status(product.pk)
    # Product stock values should be adjusted
    assert pss.logical_count == (quantity + adjust_quantity)
    # test price properties
    sa = StockAdjustment.objects.first()
    assert sa.purchase_price.currency == shop.currency
    assert sa.purchase_price.includes_tax
    sc = StockCount.objects.first()
    assert sc.stock_value.currency == shop.currency
    assert sc.stock_value.includes_tax
    assert sc.stock_unit_price.currency == shop.currency
    assert sc.stock_unit_price.includes_tax
    settings.SHUUP_ENABLE_MULTIPLE_SHOPS = True
    sa = StockAdjustment.objects.first() # refetch to invalidate cache
    assert sa.purchase_price.currency != shop.currency
    assert sa.purchase_price.currency == settings.SHUUP_HOME_CURRENCY
    assert not sa.purchase_price.includes_tax
    sc = StockCount.objects.first()
    assert sc.stock_value.currency == settings.SHUUP_HOME_CURRENCY
    assert not sc.stock_value.includes_tax
    assert sc.stock_unit_price.currency == settings.SHUUP_HOME_CURRENCY
    assert not sc.stock_unit_price.includes_tax
Example #39
0
def test_refund_entire_order():
    shop = get_default_shop()
    supplier = get_simple_supplier()
    product = create_product("test-sku",
                             shop=get_default_shop(),
                             default_price=10,
                             stock_behavior=StockBehavior.STOCKED)
    supplier.adjust_stock(product.id, 5)
    assert supplier.get_stock_statuses([product.id
                                        ])[product.id].logical_count == 5

    order = create_order_with_product(product, supplier, 2, 200, shop=shop)
    order.cache_prices()
    original_total_price = order.taxful_total_price

    assert supplier.get_stock_statuses([product.id
                                        ])[product.id].logical_count == 3

    # Create a full refund with `restock_products` set to False
    order.create_full_refund(restock_products=False)

    # Confirm the refund was created with correct amount
    assert order.taxful_total_price.amount.value == 0
    refund_line = order.lines.order_by("ordering").last()
    assert refund_line.type == OrderLineType.REFUND
    assert refund_line.taxful_price == -original_total_price

    # Make sure stock status didn't change
    assert supplier.get_stock_statuses([product.id
                                        ])[product.id].logical_count == 3

    # Delete refund line
    refund_line.delete()
    order.cache_prices()

    assert order.taxful_total_price == original_total_price

    # Create a full refund with `restock_products` set to True
    order.create_full_refund(restock_products=True)

    # Make sure product was restocked
    assert supplier.get_stock_statuses([product.id
                                        ])[product.id].logical_count == 5
Example #40
0
def test_create_full_refund(prices_include_tax):
    supplier = get_simple_supplier()
    order = _get_order(prices_include_tax, True, True)
    original_order_total = order.taxful_total_price
    num_order_lines = order.lines.count()

    order.create_full_refund(restock_products=True)

    for line in order.lines.products():
        check_stock_counts(supplier, line.product, INITIAL_PRODUCT_QUANTITY, INITIAL_PRODUCT_QUANTITY)

    assert order.has_refunds()
    assert not order.can_create_refund()
    assert not order.taxful_total_price_value
    assert not order.taxless_total_price_value
    assert order.lines.refunds().count() == num_order_lines
    assert order.shipping_status == ShippingStatus.FULLY_SHIPPED
    assert order.get_total_refunded_amount() == original_order_total.amount
    assert not order.get_total_unrefunded_amount().value
Example #41
0
def test_product_summary():
    shop = get_default_shop()
    supplier = get_simple_supplier()
    product = create_product(
        "test-sku",
        shop=get_default_shop(),
        default_price=10,
        stock_behavior=StockBehavior.STOCKED
    )
    supplier.adjust_stock(product.id, 5)

    # Order with 2 unshipped, non-refunded items and a shipping cost
    order = create_order_with_product(product, supplier, 2, 200, shop=shop)
    order.cache_prices()
    product_line = order.lines.first()
    shipping_line = order.lines.create(type=OrderLineType.SHIPPING, base_unit_price_value=5, quantity=1)

    # Make sure no invalid entries and check product quantities
    product_summary = order.get_product_summary()
    assert all(product_summary.keys())
    summary = product_summary[product.id]
    assert_defaultdict_values(summary, ordered=2, shipped=0, refunded=0, unshipped=2)

    # Create a shipment for the other item, make sure status changes
    assert order.shipping_status == ShippingStatus.NOT_SHIPPED
    assert order.can_create_shipment()
    order.create_shipment(supplier=supplier, product_quantities={product: 1})
    assert order.shipping_status == ShippingStatus.PARTIALLY_SHIPPED

    order.create_refund([{"line": shipping_line, "quantity": 1, "amount": Money(5, order.currency), "restock": False}])

    product_summary = order.get_product_summary()
    assert all(product_summary.keys())
    summary = product_summary[product.id]
    assert_defaultdict_values(summary, ordered=2, shipped=1, refunded=0, unshipped=1)

    # Create a refund for 2 items, we should get no negative values
    order.create_refund([{"line": product_line, "quantity": 2, "amount": Money(200, order.currency), "restock": False}])

    product_summary = order.get_product_summary()
    assert all(product_summary.keys())
    summary = product_summary[product.id]
    assert_defaultdict_values(summary, ordered=2, shipped=1, refunded=2, unshipped=0)
Example #42
0
def test_create_full_refund(prices_include_tax):
    supplier = get_simple_supplier()
    order = _get_order(prices_include_tax, True, True)
    original_order_total = order.taxful_total_price
    num_order_lines = order.lines.count()

    order.create_full_refund(restock_products=True)

    for line in order.lines.products():
        check_stock_counts(supplier, line.product, INITIAL_PRODUCT_QUANTITY, INITIAL_PRODUCT_QUANTITY)

    assert order.has_refunds()
    assert not order.can_create_refund()
    assert not order.taxful_total_price_value
    assert not order.taxless_total_price_value
    assert order.lines.refunds().count() == num_order_lines
    assert order.shipping_status == ShippingStatus.FULLY_SHIPPED
    assert order.get_total_refunded_amount() == original_order_total.amount
    assert not order.get_total_unrefunded_amount().value
Example #43
0
def test_refund_with_product_restock():
    shop = get_default_shop()
    supplier = get_simple_supplier()
    product = create_product("test-sku",
                             shop=get_default_shop(),
                             default_price=10,
                             stock_behavior=StockBehavior.STOCKED)
    supplier.adjust_stock(product.id, 5)
    assert supplier.get_stock_statuses([product.id
                                        ])[product.id].logical_count == 5

    order = create_order_with_product(product, supplier, 2, 200, shop=shop)
    order.cache_prices()

    assert supplier.get_stock_statuses([product.id
                                        ])[product.id].logical_count == 3

    # Create a refund with a parent line and quanity with `restock_products` set to False
    product_line = order.lines.first()
    order.create_refund([{
        "line": product_line,
        "quantity": 1,
        "restock_products": False
    }])

    assert supplier.get_stock_statuses([product.id
                                        ])[product.id].logical_count == 3
    assert order.lines.last().taxful_price == -product_line.base_unit_price
    assert order.get_total_unrefunded_amount(
    ) == product_line.base_unit_price.amount

    # Create a refund with a parent line and quanity with `restock_products` set to True
    product_line = order.lines.first()
    order.create_refund([{
        "line": product_line,
        "quantity": 1,
        "restock_products": True
    }])

    assert supplier.get_stock_statuses([product.id
                                        ])[product.id].logical_count == 4
    assert not order.taxful_total_price
Example #44
0
def test_product_copy_stock_managed(rf, admin_user):
    shop = factories.get_default_shop()
    supplier = get_simple_supplier()
    request = apply_request_middleware(rf.get("/", {}), user=admin_user)
    price = 10
    product = factories.create_product("product", shop=shop, supplier=supplier, default_price=price)

    shop_product = product.get_shop_instance(shop)

    assert Product.objects.count() == 1
    view_func = ProductCopyView.as_view()
    response = view_func(request, pk=shop_product.pk)
    if hasattr(response, "render"):
        response.render()

    assert Product.objects.count() == 2
    new_product = Product.objects.first()
    new_shop_product = new_product.get_shop_instance(shop)
    assert new_product
    assert new_product.pk != product.pk
    assert new_product.name == product.name
    assert new_shop_product
    assert new_shop_product.suppliers.first() == shop_product.suppliers.first()
    origin_product_stock_count = StockCount.objects.get_or_create(supplier=supplier, product=product)[0]
    new_product_stock_count = StockCount.objects.get_or_create(supplier=supplier, product=new_product)[0]
    assert origin_product_stock_count.stock_managed == new_product_stock_count.stock_managed

    # Make stock not managed and re-copy original product
    assert bool(origin_product_stock_count.stock_managed)  # stock managed True
    origin_product_stock_count.stock_managed = False
    origin_product_stock_count.save()

    assert Product.objects.count() == 2
    view_func = ProductCopyView.as_view()
    response = view_func(request, pk=shop_product.pk)
    if hasattr(response, "render"):
        response.render()

    assert Product.objects.count() == 3
    new_product = Product.objects.first()
    new_product_stock_count = StockCount.objects.get_or_create(supplier=supplier, product=new_product)[0]
    assert not bool(new_product_stock_count.stock_managed)
Example #45
0
def test_list_view(rf, admin_user):
    shop = factories.get_default_shop()
    supplier = get_simple_supplier()

    product = factories.create_product(sku="test", shop=shop, supplier=supplier)
    shop_product = product.get_shop_instance(shop)
    shop_product.primary_category = factories.get_default_category()
    shop_product.save()
    shop_product.categories.add(shop_product.primary_category)

    view = load("shuup.simple_supplier.admin_module.views:StocksListView").as_view()
    request = apply_request_middleware(rf.get("/", {
        "jq": json.dumps({"perPage": 100, "page": 1})
    }), user=admin_user)
    response = view(request)
    assert 200 <= response.status_code < 300

    data = json.loads(response.content.decode("utf-8"))
    for item in data["items"]:
        assert item["_url"] == ""
Example #46
0
def test_refund_entire_order_with_product_restock():
    shop = get_default_shop()
    supplier = get_simple_supplier()
    product = create_product("test-sku",
                             shop=get_default_shop(),
                             default_price=10,
                             stock_behavior=StockBehavior.STOCKED)
    supplier.adjust_stock(product.id, 5)
    check_stock_counts(supplier, product, 5, 5)

    order = create_order_with_product(product, supplier, 2, 200, shop=shop)
    order.cache_prices()

    check_stock_counts(supplier, product, 5, 3)

    # Create a full refund with `restock_products` set to True
    order.create_full_refund(restock_products=True)

    # restock logical count
    check_stock_counts(supplier, product, 5, 5)
Example #47
0
def test_simple_supplier(rf):
    supplier = get_simple_supplier()
    shop = get_default_shop()
    product = create_product("simple-test-product", shop)
    ss = supplier.get_stock_status(product.pk)
    assert ss.product == product
    assert ss.logical_count == 0

    product_qty = 5
    shipment = Shipment.objects.create(supplier=supplier, type=ShipmentType.IN)
    ShipmentProduct.objects.create(shipment=shipment, product=product, quantity=product_qty)

    assert shipment.status == ShipmentStatus.NOT_SENT

    shipment.set_received()
    assert shipment.status == ShipmentStatus.RECEIVED

    ss = supplier.get_stock_status(product.pk)
    assert ss.product == product
    assert ss.logical_count == 5
Example #48
0
def test_new_product_admin_form_renders(rf, client, admin_user):
    """
    Make sure that no exceptions are raised when creating a new product
    with simple supplier enabled
    """
    shop = get_default_shop()
    request = apply_request_middleware(rf.get("/"), user=admin_user)
    view = ProductEditView.as_view()
    supplier = get_simple_supplier()
    supplier.stock_managed = True
    supplier.save()

    # This should not raise an exception
    view(request).render()

    supplier.stock_managed = False
    supplier.save()

    # Nor should this
    view(request).render()
Example #49
0
def test_process_stock_managed(rf, admin_user):
    supplier = get_simple_supplier(stock_managed=False)
    shop = get_default_shop()
    product = create_product("simple-test-product", shop)
    request = apply_request_middleware(rf.get("/",
                                              data={"stock_managed": True}),
                                       user=admin_user)

    with pytest.raises(Exception) as ex:
        # Should raise exception becasue only POST is allowed
        response = process_stock_managed(request, supplier.id, product.id)

    request = apply_request_middleware(rf.post("/",
                                               data={"stock_managed": True}),
                                       user=admin_user)
    response = process_stock_managed(request, supplier.id, product.id)
    assert response.status_code == 200

    # Check no stock count
    sc = StockCount.objects.filter(supplier=supplier, product=product).first()
    assert sc.logical_count == 0
    # Check stock count managed by default
    assert sc.stock_managed == True
    # Now test with stock managed turned off
    request = apply_request_middleware(rf.post("/",
                                               data={"stock_managed": False}),
                                       user=admin_user)
    response = process_stock_managed(request, supplier.id, product.id)
    assert response.status_code == 200
    # Check stock management is disabled for product
    sc = StockCount.objects.filter(supplier=supplier, product=product).first()
    assert sc.stock_managed == False
    # Now test with stock managed turned on
    request = apply_request_middleware(rf.post("/",
                                               data={"stock_managed": True}),
                                       user=admin_user)
    response = process_stock_managed(request, supplier.id, product.id)
    assert response.status_code == 200
    # Check stock management is enabled for product
    sc = StockCount.objects.filter(supplier=supplier, product=product).first()
    assert sc.stock_managed == True
Example #50
0
def test_supplier_with_stock_counts(rf, admin_user):
    supplier = get_simple_supplier()
    shop = get_default_shop()
    product = create_product("simple-test-product", shop, supplier)
    quantity = random.randint(100, 600)
    supplier.adjust_stock(product.pk, quantity)
    adjust_quantity = random.randint(100, 600)
    request = rf.get("/")
    request.user = admin_user
    request.POST = {
        "purchase_price": decimal.Decimal(32.00),
        "delta": adjust_quantity
    }
    response = process_stock_adjustment(request, supplier.id, product.id)
    assert response.status_code == 400  # Only POST is allowed
    request.method = "POST"
    response = process_stock_adjustment(request, supplier.id, product.id)
    assert response.status_code == 200
    pss = supplier.get_stock_status(product.pk)
    # Product stock values should be adjusted
    assert pss.logical_count == (quantity + adjust_quantity)
Example #51
0
def test_admin_form(rf, admin_user):
    supplier = get_simple_supplier()
    shop = get_default_shop()
    product = create_product("simple-test-product", shop, supplier)
    request = apply_request_middleware(rf.get("/"), user=admin_user)
    frm = SimpleSupplierForm(product=product, request=request)
    # Form contains 1 product even if the product is not stocked
    assert len(frm.products) == 1

    # Now since product is stocked it should be in the form
    frm = SimpleSupplierForm(product=product, request=request)
    assert len(frm.products) == 1

    # Add stocked children for product
    child_product = create_product("child-test-product", shop, supplier)
    child_product.link_to_parent(product)

    # Admin form should now contain only child products for product
    frm = SimpleSupplierForm(product=product, request=request)
    assert len(frm.products) == 1
    assert frm.products[0] == child_product
Example #52
0
def test_simple_supplier_out_of_stock(rf, anonymous):
    supplier = get_simple_supplier()
    shop = get_default_shop()
    product = create_product("simple-test-product",
                             shop,
                             supplier,
                             stock_behavior=StockBehavior.STOCKED)

    if anonymous:
        customer = AnonymousContact()
    else:
        customer = create_random_person()

    ss = supplier.get_stock_status(product.pk)
    assert ss.product == product
    assert ss.logical_count == 0
    num = random.randint(100, 500)
    supplier.adjust_stock(product.pk, +num)
    assert supplier.get_stock_status(product.pk).logical_count == num

    shop_product = product.get_shop_instance(shop)
    assert shop_product.is_orderable(supplier, customer, 1)

    # Create order
    order = create_order_with_product(product, supplier, num, 3, shop=shop)
    order.get_product_ids_and_quantities()
    pss = supplier.get_stock_status(product.pk)
    assert pss.logical_count == 0
    assert pss.physical_count == num

    assert not shop_product.is_orderable(supplier, customer, 1)

    # Create shipment
    shipment = order.create_shipment_of_all_products(supplier)
    assert isinstance(shipment, Shipment)
    pss = supplier.get_stock_status(product.pk)
    assert pss.logical_count == 0
    assert pss.physical_count == 0

    assert not shop_product.is_orderable(supplier, customer, 1)
Example #53
0
def test_refund_entire_order_without_restock(admin_user):
    shop = get_default_shop()
    supplier = get_simple_supplier()
    product = create_product(
        "test-sku",
        shop=get_default_shop(),
        default_price=10,
    )
    supplier.adjust_stock(product.id, 5)
    _check_stock_counts(supplier, product, 5, 5)

    order = create_order_with_product(product,
                                      supplier,
                                      2,
                                      200,
                                      Decimal("0.24"),
                                      shop=shop)
    order.payment_status = PaymentStatus.DEFERRED
    order.cache_prices()
    order.save()

    original_total_price = order.taxful_total_price
    _check_stock_counts(supplier, product, 5, 3)

    client = _get_client(admin_user)
    refund_url = "/api/shuup/order/%s/create_full_refund/" % order.id
    data = {"restock_products": False}
    response = client.post(refund_url, data, format="json")
    assert response.status_code == status.HTTP_201_CREATED
    order.refresh_from_db()

    # Confirm the refund was created with correct amount
    assert order.taxless_total_price.amount.value == 0
    assert order.taxful_total_price.amount.value == 0
    refund_line = order.lines.order_by("ordering").last()
    assert refund_line.type == OrderLineType.REFUND
    assert refund_line.taxful_price == -original_total_price

    # Make sure logical count reflects refunded products
    _check_stock_counts(supplier, product, 5, 3)
Example #54
0
def test_order_source(rf, admin_user):
    """
    Test order source validation with stocked products.
    """
    shop = get_default_shop()
    supplier = get_simple_supplier()
    product = create_product("simple-test-product", shop, supplier)
    product.stock_behavior = StockBehavior.STOCKED
    product.save()
    quantity = 345
    supplier.adjust_stock(product.pk, quantity)
    assert supplier.get_stock_statuses(
        [product.id])[product.id].logical_count == quantity
    assert not list(
        supplier.get_orderability_errors(
            product.get_shop_instance(shop), quantity, customer=None))
    assert list(
        supplier.get_orderability_errors(product.get_shop_instance(shop),
                                         quantity + 1,
                                         customer=None))

    source = seed_source(admin_user, shop)
    source.add_line(
        type=OrderLineType.PRODUCT,
        product=product,
        supplier=supplier,
        quantity=quantity,
        base_unit_price=source.create_price(10),
    )
    assert not list(source.get_validation_errors())

    source.add_line(
        type=OrderLineType.PRODUCT,
        product=product,
        supplier=supplier,
        quantity=quantity,
        base_unit_price=source.create_price(10),
    )
    assert list(source.get_validation_errors())
Example #55
0
def test_shipment_with_insufficient_stock(stock_managed):
    if "shuup.simple_supplier" not in settings.INSTALLED_APPS:
        pytest.skip("Need shuup.simple_supplier in INSTALLED_APPS")

    from shuup_tests.simple_supplier.utils import get_simple_supplier

    shop = get_default_shop()
    supplier = get_simple_supplier(stock_managed=stock_managed)

    order = _get_order(shop, supplier, stocked=True)
    product_line = order.lines.products().first()
    product = product_line.product
    assert product_line.quantity == 15

    supplier.adjust_stock(product.pk, delta=10)
    stock_status = supplier.get_stock_status(product.pk)
    assert stock_status.physical_count == 10

    order.create_shipment({product: 5}, supplier=supplier)

    # mark all shipments as sent
    order.shipments.update(status=ShipmentStatus.SENT)
    order.update_shipping_status()

    assert order.shipping_status == ShippingStatus.PARTIALLY_SHIPPED
    assert order.shipments.all().count() == 1

    if stock_managed:
        # Stock error should be only raised for stock managed supplier
        supplier.stock_managed = True
        supplier.save()

        with pytest.raises(NoProductsToShipException):
            order.create_shipment({product: 10}, supplier=supplier)

        # Should be fine after adding more stock
        supplier.adjust_stock(product.pk, delta=5)

    order.create_shipment({product: 10}, supplier=supplier)
Example #56
0
def test_can_create_shipment():
    shop = get_default_shop()
    supplier = get_simple_supplier()
    product = create_product("test-sku",
                             shop=get_default_shop(),
                             default_price=10,
                             stock_behavior=StockBehavior.STOCKED)
    supplier.adjust_stock(product.id, 10)

    order = create_order_with_product(product, supplier, 1, 200, shop=shop)
    assert order.can_create_shipment()

    # Fully shipped orders can't create shipments
    order.create_shipment_of_all_products(supplier)
    assert not order.can_create_shipment()

    order = create_order_with_product(product, supplier, 1, 200, shop=shop)
    assert order.can_create_shipment()

    # Canceled orders can't create shipments
    order.set_canceled()
    assert not order.can_create_shipment()
Example #57
0
def test_get_listed_products_orderable_only():
    context = get_jinja_context()
    shop = get_default_shop()
    simple_supplier = get_simple_supplier()
    n_products = 2

    # Create product without stock
    product = create_product(
        "test-sku",
        supplier=simple_supplier,
        shop=shop,
        stock_behavior=StockBehavior.STOCKED
    )

    from shuup.front.template_helpers import general

    for cache_test in range(2):
        assert len(general.get_listed_products(context, n_products, orderable_only=True)) == 0

    for cache_test in range(2):
        assert len(general.get_listed_products(context, n_products, orderable_only=False)) == 1

    # Increase stock on product
    quantity = product.get_shop_instance(shop).minimum_purchase_quantity
    simple_supplier.adjust_stock(product.id, quantity)
    for cache_test in range(2):
        assert len(general.get_listed_products(context, n_products, orderable_only=True)) == 1

    for cache_test in range(2):
        assert len(general.get_listed_products(context, n_products, orderable_only=False)) == 1

    # Decrease stock on product
    simple_supplier.adjust_stock(product.id, -quantity)
    for cache_test in range(2):
        assert len(general.get_listed_products(context, n_products, orderable_only=True)) == 0

    for cache_test in range(2):
        assert len(general.get_listed_products(context, n_products, orderable_only=False)) == 1
Example #58
0
def test_quantity_has_to_be_in_stock(admin_user, settings):
    configure(settings)
    from shuup_tests.simple_supplier.utils import get_simple_supplier
    from shuup.core.models import StockBehavior
    shop = factories.get_default_shop()
    basket = factories.get_basket()

    supplier = get_simple_supplier()
    product = factories.create_product("simple-test-product", shop, supplier)
    quantity = 256
    supplier.adjust_stock(product.pk, quantity)
    product.stock_behavior = StockBehavior.STOCKED
    product.save()
    shop_product = product.shop_products.first()
    shop_product.suppliers.add(supplier)
    shop_product.save()

    client = _get_client(admin_user)
    payload = {'shop': shop.id, 'product': shop_product.id, 'quantity': 493020}
    response = client.post(
        '/api/shuup/basket/{}-{}/add/'.format(shop.pk, basket.key), payload)
    assert response.status_code == status.HTTP_400_BAD_REQUEST
    assert '"Insufficient stock"' in str(response.content)
Example #59
0
def test_refund_entire_order_with_product_restock():
    if "shuup.simple_supplier" not in settings.INSTALLED_APPS:
        pytest.skip("Need shuup.simple_supplier in INSTALLED_APPS")
    from shuup_tests.simple_supplier.utils import get_simple_supplier
    shop = get_default_shop()
    supplier = get_simple_supplier()
    product = create_product("test-sku",
                             shop=get_default_shop(),
                             default_price=10,
                             stock_behavior=StockBehavior.STOCKED)
    supplier.adjust_stock(product.id, 5)
    check_stock_counts(supplier, product, 5, 5)

    order = create_order_with_product(product, supplier, 2, 200, shop=shop)
    order.cache_prices()

    check_stock_counts(supplier, product, 5, 3)

    # Create a full refund with `restock_products` set to True
    order.create_full_refund(restock_products=True)

    # restock logical count
    check_stock_counts(supplier, product, 5, 5)