Exemple #1
0
def test_product_purchasable_media():
    shop = get_default_shop()
    supplier = get_simple_supplier()
    product = create_product(
        "test-sku",
        shop=get_default_shop(),
        default_price=10,
    )
    medias = add_product_image(product, True)
    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.create_shipment_of_all_products(supplier=supplier)
    order.shipping_status = ShippingStatus.FULLY_SHIPPED
    order.create_payment(order.taxful_total_price)
    currency = order.currency
    assert order.payments.exists(), "A payment was created"
    with pytest.raises(NoPaymentToCreateException):
        order.create_payment(Money(6, currency))

    order.save()
    assert order.is_paid()
    assert order.get_purchased_attachments().count() == len(medias)
def test_create_full_refund_view(rf, admin_user):
    shop = get_default_shop()
    supplier = get_default_supplier()
    product = create_product(sku="test-sku", shop=shop, supplier=supplier, default_price=3.33)
    order = create_order_with_product(product, supplier, quantity=1, taxless_base_unit_price=1, shop=shop)
    order.cache_prices()

    original_total_price = order.taxful_total_price

    assert not order.has_refunds()
    assert len(order.lines.all()) == 1
    assert order.taxful_total_price.amount.value != 0

    data = {
        "restock_products": "on",
    }

    request = apply_request_middleware(rf.post("/", data=data), user=admin_user)
    view = OrderCreateFullRefundView.as_view()
    response = view(request, pk=order.pk)
    assert response.status_code == 302
    assert order.has_refunds()
    order.cache_prices()

    assert order.taxful_total_price.amount.value == 0
    refund_line = order.lines.filter(type=OrderLineType.REFUND).last()
    assert refund_line
    assert refund_line.taxful_price == -original_total_price
def test_create_refund_view(rf, admin_user):
    shop = get_default_shop()
    supplier = get_default_supplier()
    product = create_product(sku="test-sku", shop=shop, supplier=supplier, default_price=3.33)
    order = create_order_with_product(product, supplier, quantity=1, taxless_base_unit_price=1, shop=shop)
    order.cache_prices()
    order.save()

    assert not order.has_refunds()
    assert len(order.lines.all()) == 1

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

    request = apply_request_middleware(rf.post("/", data=data), user=admin_user)
    view = OrderCreateRefundView.as_view()
    response = view(request, pk=order.pk)
    assert response.status_code == 302
    assert order.has_refunds()

    assert len(order.lines.all()) == 2
    refund_line = order.lines.filter(type=OrderLineType.REFUND).last()
    assert refund_line
    assert refund_line.taxful_price == -product_line.taxful_price
def test_tracking_codes():
    product = get_default_product()
    supplier = get_default_supplier()
    order = create_order_with_product(
        product,
        supplier=supplier,
        quantity=1,
        taxless_base_unit_price=10,
        tax_rate=decimal.Decimal("0.5")
    )
    _add_product_to_order(order, "duck-tape-1", 3, order.shop, supplier)
    _add_product_to_order(order, "water-1", 2, order.shop, supplier)

    order.cache_prices()
    order.check_all_verified()
    order.save()

    # Create shipment with tracking code for every product line.
    product_lines = order.lines.exclude(product_id=None)
    assert len(product_lines) == 3
    for line in product_lines:
        shipment = order.create_shipment({line.product: line.quantity}, supplier=supplier)
        if line.quantity != 3:
            shipment.tracking_code = "123FI"
            shipment.save()

    tracking_codes = order.get_tracking_codes()
    code_count = (len(product_lines)-1)  # We skipped that one
    assert len(tracking_codes) == code_count
    assert len([tracking_code for tracking_code in tracking_codes if tracking_code == "123FI"]) == code_count
Exemple #5
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)
Exemple #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,
    )
    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)
Exemple #7
0
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
Exemple #8
0
def _create_total_paid_sales(shop, day):
    product = create_product("test", shop=shop)
    supplier = get_default_supplier()
    order = create_order_with_product(product, supplier, 1, 10, shop=shop)
    order.order_date = day
    order.save()
    order.create_payment(order.taxful_total_price)
    assert order.is_paid()
Exemple #9
0
def test_can_create_payment():
    shop = get_default_shop()
    supplier = get_default_supplier()
    product = create_product(
        "test-sku",
        shop=get_default_shop(),
        default_price=10,
    )

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

    # Partially paid orders can create payments
    payment_amount = (order.taxful_total_price.amount / 2)
    order.create_payment(payment_amount)
    assert order.can_create_payment()

    # But fully paid orders can't
    remaining_amount = order.taxful_total_price.amount - payment_amount
    order.create_payment(remaining_amount)
    assert not order.can_create_payment()

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

    # Canceled orders can't create payments
    order.set_canceled()
    assert not order.can_create_payment()

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

    # Partially refunded orders can create payments
    order.create_refund([
        {"line": order.lines.first(), "quantity": 1, "amount": Money(200, order.currency), "restock": False}])
    assert order.can_create_payment()

    # But fully refunded orders can't
    order.create_refund([
        {"line": order.lines.first(), "quantity": 1, "amount": Money(200, order.currency), "restock": False}])
    assert not order.can_create_payment()
Exemple #10
0
def test_order_verification():
    product = get_default_product()
    supplier = get_default_supplier()
    order = create_order_with_product(product, supplier=supplier, quantity=3, n_lines=10, taxless_base_unit_price=10)
    order.require_verification = True
    order.save()
    assert not order.check_all_verified(), "Nothing is verified by default"
    order.lines.filter(pk=order.lines.filter(verified=False).first().pk).update(verified=True)
    assert not order.check_all_verified(), "All is not verified even if something is"
    order.lines.all().update(verified=True)
    assert order.check_all_verified(), "All is now verified"
    assert not order.require_verification, "Verification complete"
Exemple #11
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,
    )
    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()
def test_edit_view_with_anonymous_contact(rf, admin_user):
    shop = get_default_shop()
    supplier = get_default_supplier()
    product = create_product(
        sku=printable_gibberish(),
        supplier=supplier,
        shop=shop
    )
    order = create_order_with_product(product, supplier, 1, 10, shop=shop)
    order.save()
    assert not order.customer
    request = apply_request_middleware(rf.get("/"), user=admin_user)
    response = OrderEditView.as_view()(request=request, pk=order.pk)
    assert response.status_code == 200
Exemple #13
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)
Exemple #14
0
def test_basic_order():
    PRODUCTS_TO_SEND = 10
    product = get_default_product()
    supplier = get_default_supplier()
    order = create_order_with_product(
        product,
        supplier=supplier,
        quantity=PRODUCTS_TO_SEND,
        taxless_base_unit_price=10,
        tax_rate=Decimal("0.5")
    )
    assert order.shop.prices_include_tax is False
    price = order.shop.create_price
    currency = order.currency

    discount_order_line = OrderLine(order=order, quantity=1, type=OrderLineType.OTHER)
    discount_order_line.discount_amount = price(30)
    assert discount_order_line.price == price(-30)
    discount_order_line.save()

    order.cache_prices()
    order.check_all_verified()
    order.save()
    assert order.taxful_total_price == TaxfulPrice(PRODUCTS_TO_SEND * (10 + 5) - 30, currency)
    shipment = order.create_shipment_of_all_products(supplier=supplier)
    assert shipment.total_products == PRODUCTS_TO_SEND, "All products were shipped"
    assert shipment.weight == product.gross_weight * PRODUCTS_TO_SEND / 1000, "Gravity works"
    assert not order.get_unshipped_products(), "Nothing was left in the warehouse"
    order.shipping_status = ShippingStatus.FULLY_SHIPPED
    order.create_payment(order.taxful_total_price)
    assert order.payments.exists(), "A payment was created"
    with pytest.raises(NoPaymentToCreateException):
        order.create_payment(Money(6, currency))
    assert order.is_paid(), "Order got paid"
    assert order.can_set_complete(), "Finalization is possible"
    order.status = OrderStatus.objects.get_default_complete()
    assert order.is_complete(), "Finalization done"

    summary = order.get_tax_summary()
    assert len(summary) == 2
    assert summary[0].tax_rate * 100 == 50
    assert summary[0].based_on == Money(100, currency)
    assert summary[0].tax_amount == Money(50, currency)
    assert summary[0].taxful == summary[0].based_on + summary[0].tax_amount
    assert summary[1].tax_id is None
    assert summary[1].tax_code == ''
    assert summary[1].tax_amount == Money(0, currency)
    assert summary[1].tax_rate == 0
    assert order.get_total_tax_amount() == Money(50, currency)
Exemple #15
0
def test_process_payment_return_request(rf, get_payment_processor, expected_final_payment_status):
    """
    Order payment with default payment method with ``CustomPaymentProcessor``
    provider should remain NOT_PAID.

    Order payment with default payment method with ``PaymentWithCheckoutPhase``
    provider should become DEFERRED.

    Payment can't be processed if method doesn't have provider or provider
    is not enabled or payment method is not enabled.
    """
    payment_processor = get_payment_processor()
    pm = PaymentMethod.objects.create(
        shop=get_default_shop(), name="Test method", enabled=False, tax_class=get_default_tax_class())
    product = create_product(sku="test-sku", shop=get_default_shop(), default_price=100)
    order = create_order_with_product(
        product=product,
        supplier=get_default_supplier(),
        quantity=1,
        taxless_base_unit_price=Decimal('5.55'),
        shop=get_default_shop()
    )
    order.payment_method = pm
    order.save()
    assert order.payment_status == PaymentStatus.NOT_PAID
    with pytest.raises(ValueError):  # Can't process payment with unusable method
        order.payment_method.process_payment_return_request(order, rf.get("/"))
    assert order.payment_status == PaymentStatus.NOT_PAID
    pm.payment_processor = payment_processor
    pm.payment_processor.enabled = False
    pm.save()

    with pytest.raises(ValueError):  # Can't process payment with unusable method
        order.payment_method.process_payment_return_request(order, rf.get("/"))
    assert order.payment_status == PaymentStatus.NOT_PAID
    pm.payment_processor.enabled = True
    pm.save()

    with pytest.raises(ValueError):  # Can't process payment with unusable method
        order.payment_method.process_payment_return_request(order, rf.get("/"))
    assert order.payment_status == PaymentStatus.NOT_PAID
    pm.enabled = True
    pm.save()

    # Add payment data for checkout phase
    order.payment_data = {"input_value": True}
    order.payment_method.process_payment_return_request(order, rf.get("/"))
    assert order.payment_status == expected_final_payment_status
Exemple #16
0
def test_refunds_rounding_multiple_partial_refund():
    shop = get_default_shop()
    supplier = get_default_supplier()
    product = create_product(
        "test-sku",
        shop=get_default_shop(),
        default_price=29.264,
    )
    order = create_order_with_product(product, supplier, 2, 29.264, shop=shop)
    order.cache_prices()
    assert len(order.lines.all()) == 1

    line = order.lines.first()
    order.create_refund([{"line": line, "quantity": 1, "amount": Money("29.26", order.currency)}])
    assert order.taxful_total_price == order.shop.create_price("29.27")
    order.create_refund([{"line": line, "quantity": 1, "amount": Money("29.27", order.currency)}])
    assert line.max_refundable_amount == Money("0", order.currency)
    assert order.taxful_total_price == order.shop.create_price(0)
Exemple #17
0
def test_refund_entire_order_restock_shipment_no_supplier_module():
    shop = get_default_shop()
    supplier = get_default_supplier()
    product = create_product(
        "test-sku",
        shop=get_default_shop(),
        default_price=10,
    )
    check_stock_counts(supplier, product, 0, 0)
    order = create_order_with_product(product, supplier, 2, 200, shop=shop)
    product_line = order.lines.first()
    order.create_shipment({product_line.product: 2}, supplier=supplier)
    check_stock_counts(supplier, product, 0, 0)

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

    check_stock_counts(supplier, product, 0, 0)
Exemple #18
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,
    )
    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)
Exemple #19
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()
Exemple #20
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,
    )
    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)
def test_arbitrary_refund_availability(rf, admin_user):
    shop = get_default_shop()
    supplier = get_default_supplier()
    product = create_product(sku="test-sku", shop=shop, supplier=supplier, default_price=3.33)
    order = create_order_with_product(product, supplier, quantity=1, taxless_base_unit_price=1, shop=shop)
    order.cache_prices()
    order.save()

    assert not order.has_refunds()
    assert len(order.lines.all()) == 1

    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 force_text(response.render().content)

    refund_option_str = '<option value="amount">Refund arbitrary amount</option>'
    assert refund_option_str in get_refund_view_content()
    with override_settings(E-Commerce_ALLOW_ARBITRARY_REFUNDS=False):
        assert refund_option_str not in get_refund_view_content()
Exemple #22
0
def test_payments():
    shop = get_default_shop()
    supplier = get_default_supplier()
    product = create_product("test-sku", shop=get_default_shop(), default_price=10)
    order = create_order_with_product(product, supplier, 1, 200, shop=shop)
    order.cache_prices()

    assert order.get_total_paid_amount().value == 0
    assert order.get_total_unpaid_amount().value == order.taxful_total_price.value

    assert order.payment_status == PaymentStatus.NOT_PAID
    assert order.can_edit()

    partial_payment_amount = order.taxful_total_price / 2
    remaining_amount = order.taxful_total_price - partial_payment_amount
    order.create_payment(partial_payment_amount)
    assert order.payment_status == PaymentStatus.PARTIALLY_PAID
    assert not order.can_edit()

    order.create_payment(remaining_amount)
    assert order.payment_status == PaymentStatus.FULLY_PAID
    assert not order.can_edit()
Exemple #23
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)
Exemple #24
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
Exemple #25
0
def test_can_create_refund():
    shop = get_default_shop()
    supplier = get_default_supplier()
    product = create_product(
        "test-sku",
        shop=get_default_shop(),
        default_price=10,
    )

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

    # Partially refunded orders can create refunds
    order.create_refund([
        {"line": order.lines.first(), "quantity": 1, "amount": Money(200, order.currency), "restock": False}])
    assert order.can_create_refund()

    # But fully refunded orders can't
    order.create_refund([
        {"line": order.lines.first(), "quantity": 1, "amount": Money(200, order.currency), "restock": False}])
    assert not order.can_create_refund()
Exemple #26
0
def test_max_refundable_amount():
    shop = get_default_shop()
    supplier = get_default_supplier()
    product = create_product(
        "test-sku",
        shop=get_default_shop(),
        default_price=10,
    )
    order = create_order_with_product(product, supplier, 2, 200, shop=shop)
    order.cache_prices()
    assert len(order.lines.all()) == 1

    line = order.lines.first()
    assert line.max_refundable_amount == line.taxful_price.amount

    partial_refund_amount = Money(10, order.currency)
    assert partial_refund_amount.value > 0

    order.create_refund([{"line": line, "quantity": 1, "amount": partial_refund_amount}])
    assert line.max_refundable_amount == line.taxful_price.amount - partial_refund_amount
    assert order.get_total_tax_amount() == Money(
        order.taxful_total_price_value - order.taxless_total_price_value,
        order.currency)
def test_taxes_report(rf):
    shop = get_default_shop()
    supplier = get_default_supplier()
    product1 = create_product("p1", shop=shop, supplier=supplier)
    product2 = create_product("p2", shop=shop, supplier=supplier)
    create_product("p3", shop=shop, supplier=supplier)
    tax_rate1 = Decimal("0.3")
    tax_rate2 = Decimal("0.45")

    tax_rate1_instance = get_test_tax(tax_rate1)
    tax_rate2_instance = get_test_tax(tax_rate2)

    # orders for person 1
    person1 = create_random_person()
    order1 = create_order_with_product(product=product1, supplier=supplier, quantity=2,
                                       taxless_base_unit_price="5", tax_rate=tax_rate1, n_lines=1, shop=shop)
    order1.customer = person1
    order1.save()
    order2 = create_order_with_product(product=product2, supplier=supplier, quantity=1,
                                       taxless_base_unit_price="10", tax_rate=tax_rate1, n_lines=1, shop=shop)
    order2.customer = person1
    order2.save()

    # orders for person 2
    person2 = create_random_person()
    order3 = create_order_with_product(product=product1, supplier=supplier, quantity=1,
                                       taxless_base_unit_price="2", tax_rate=tax_rate2, n_lines=1, shop=shop)
    order3.customer = person2
    order3.save()

    order4 = create_order_with_product(product=product2, supplier=supplier, quantity=2,
                                       taxless_base_unit_price="8", tax_rate=tax_rate1, n_lines=1, shop=shop)
    order4.customer = person2
    order4.save()

    # pay orders
    [o.create_payment(o.taxful_total_price) for o in Order.objects.all()]

    data = {
        "report": TaxesReport.get_name(),
        "shop": shop.pk,
        "date_range": DateRangeChoices.ALL_TIME,
        "writer": "json",
        "force_download": 1,
    }
    report = TaxesReport(**data)
    writer = get_writer_instance(data["writer"])
    response = writer.get_response(report=report)
    if hasattr(response, "render"):
        response.render()
    json_data = json.loads(response.content.decode("utf-8"))
    assert force_text(TaxesReport.title) in json_data.get("heading")
    data = json_data.get("tables")[0].get("data")
    assert len(data) == 2

    tax1_rate1_total = (
        (order1.taxful_total_price_value - order1.taxless_total_price_value) +
        (order2.taxful_total_price_value - order2.taxless_total_price_value) +
        (order4.taxful_total_price_value - order4.taxless_total_price_value)
    )
    tax1_pretax_total = (
        order1.taxless_total_price_value +
        order2.taxless_total_price_value +
        order4.taxless_total_price_value
    )
    tax1_total = (
        order1.taxful_total_price_value +
        order2.taxful_total_price_value +
        order4.taxful_total_price_value
    )
    tax2_rate2_total = (order3.taxful_total_price_value - order3.taxless_total_price_value)

    # the report data order is the total charged ascending
    expected_result = [
        {
            "tax": tax_rate2_instance.name,
            "tax_rate": tax_rate2,
            "order_count": 1,
            "total_pretax_amount": order3.taxless_total_price_value,
            "total_tax_amount": tax2_rate2_total,
            "total": order3.taxful_total_price_value,
        },
        {
            "tax": tax_rate1_instance.name,
            "tax_rate": tax_rate1,
            "order_count": 3,
            "total_pretax_amount": tax1_pretax_total,
            "total_tax_amount": tax1_rate1_total,
            "total": tax1_total,
        }
    ]
    for ix, tax in enumerate(data):
        assert tax["tax"] == expected_result[ix]["tax"]
        assert Decimal(tax["tax_rate"]) == expected_result[ix]["tax_rate"] * Decimal(100.0)
        assert tax["order_count"] == str(expected_result[ix]["order_count"])
        assert tax["total_tax_amount"] == str(expected_result[ix]["total_tax_amount"])
        assert tax["total_pretax_amount"] == str(expected_result[ix]["total_pretax_amount"])
        assert tax["total"] == str(expected_result[ix]["total"])
def test_customer_sales_report(rf, order_by):
    shop = get_default_shop()
    supplier = get_default_supplier()
    product1 = create_product("p1", shop=shop, supplier=supplier)
    product2 = create_product("p2", shop=shop, supplier=supplier)
    product3 = create_product("p3", shop=shop, supplier=supplier)
    tax_rate = Decimal("0.3")

    # orders for person 1
    person1 = create_random_person()
    order1 = create_order_with_product(product=product1, supplier=supplier, quantity=2,
                                       taxless_base_unit_price="5", tax_rate=tax_rate, n_lines=1, shop=shop)
    order1.customer = person1
    order1.save()
    order2 = create_order_with_product(product=product2, supplier=supplier, quantity=1,
                                       taxless_base_unit_price="10", n_lines=1, shop=shop)
    order2.customer = person1
    order2.save()

    person1_taxful_total_sales = (order1.taxful_total_price + order2.taxful_total_price)
    person1_taxless_total_sales = (order1.taxless_total_price + order2.taxless_total_price)
    person1_avg_sales = (person1_taxful_total_sales / Decimal(2.0))

    # orders for person 2
    person2 = create_random_person()
    order3 = create_order_with_product(product=product1, supplier=supplier, quantity=2,
                                       taxless_base_unit_price="5", tax_rate=tax_rate, n_lines=1, shop=shop)
    order3.customer = person2
    order3.save()

    order4 = create_order_with_product(product=product2, supplier=supplier, quantity=2,
                                       taxless_base_unit_price="50", n_lines=1, shop=shop)
    order4.customer = person2
    order4.save()

    order5 = create_order_with_product(product=product3, supplier=supplier, quantity=2,
                                       taxless_base_unit_price="20", tax_rate=tax_rate, n_lines=1, shop=shop)
    order5.customer = person2
    order5.save()
    person2_taxful_total_sales = (order3.taxful_total_price + order4.taxful_total_price + order5.taxful_total_price)
    person2_taxless_total_sales = (order3.taxless_total_price + order4.taxless_total_price + order5.taxless_total_price)
    person2_avg_sales = (person2_taxful_total_sales / Decimal(3.0)).quantize(Decimal('0.01'))

    # pay orders
    [o.create_payment(o.taxful_total_price) for o in Order.objects.all()]

    data = {
        "report": CustomerSalesReport.get_name(),
        "shop": shop.pk,
        "date_range": DateRangeChoices.ALL_TIME,
        "writer": "json",
        "force_download": 1,
        "order_by": order_by
    }
    report = CustomerSalesReport(**data)
    writer = get_writer_instance(data["writer"])
    response = writer.get_response(report=report)
    if hasattr(response, "render"):
        response.render()
    json_data = json.loads(response.content.decode("utf-8"))
    assert force_text(CustomerSalesReport.title) in json_data.get("heading")
    data = json_data.get("tables")[0].get("data")

    assert len(data) == 2

    if order_by == "order_count":
        person1_data = data[1]
        person2_data = data[0]

    elif order_by == "average_sales":
        if person1_avg_sales > person2_avg_sales:
            person1_data = data[0]
            person2_data = data[1]
        else:
            person1_data = data[1]
            person2_data = data[0]

    elif order_by == "taxless_total":
        if person1_taxless_total_sales > person2_taxless_total_sales:
            person1_data = data[0]
            person2_data = data[1]
        else:
            person1_data = data[1]
            person2_data = data[0]

    elif order_by == "taxful_total":
        if person1_taxful_total_sales > person2_taxful_total_sales:
            person1_data = data[0]
            person2_data = data[1]
        else:
            person1_data = data[1]
            person2_data = data[0]

    assert person1_data["customer"] == person1.name
    assert person1_data["order_count"] == "2"
    assert person1_data["average_sales"] == str(person1_avg_sales.value)
    assert person1_data["taxless_total"] == str(person1_taxless_total_sales.value.quantize(Decimal("0.01")))
    assert person1_data["taxful_total"] == str(person1_taxful_total_sales.value.quantize(Decimal("0.01")))

    assert person2_data["customer"] == person2.name
    assert person2_data["order_count"] == "3"
    assert person2_data["average_sales"] == str(person2_avg_sales.value)
    assert person2_data["taxless_total"] == str(person2_taxless_total_sales.value.quantize(Decimal("0.01")))
    assert person2_data["taxful_total"] == str(person2_taxful_total_sales.value.quantize(Decimal("0.01")))
def test_product_total_sales_report(rf, admin_user, order_by):
    with override_provides("reports", ["E-Commerce.default_reports.reports.product_total_sales:ProductSalesReport"]):
        shop = get_default_shop()
        supplier = get_default_supplier()
        product1 = create_product("product1", supplier=supplier, shop=shop)
        product2 = create_product("product2", supplier=supplier, shop=shop)

        p1_qtd, p1_price, p1_tr, p1_lines = Decimal(3), Decimal(5), Decimal(0), 5
        p2_qtd, p2_price, p2_tr, p2_lines = Decimal(4), Decimal(5), Decimal(0.95), 3

        order = create_order_with_product(
            product=product1, supplier=supplier, quantity=p1_qtd,
            taxless_base_unit_price=p1_price, tax_rate=p1_tr, n_lines=p1_lines, shop=shop)
        order.create_payment(order.taxful_total_price.amount)

        order2 = create_order_with_product(
            product=product2, supplier=supplier, quantity=p2_qtd,
            taxless_base_unit_price=p2_price, tax_rate=p2_tr, n_lines=p2_lines, shop=shop)
        order2.create_payment(order2.taxful_total_price.amount)

        data = {
            "report": ProductSalesReport.get_name(),
            "shop": shop.pk,
            "date_range": DateRangeChoices.ALL_TIME.value,
            "writer": "json",
            "force_download": 1,
            "order_by": order_by
        }

        view = ReportView.as_view()
        request = apply_request_middleware(rf.post("/", data=data), user=admin_user)
        response = view(request)
        if hasattr(response, "render"):
            response.render()
        assert response.status_code == 200
        json_data = json.loads(response.content.decode("utf-8"))
        assert force_text(ProductSalesReport.title) in json_data.get("heading")

        data = json_data["tables"][0]["data"]
        assert len(data) == 2

        p1_total_qtd = p1_qtd * p1_lines
        p1_taxless_total = p1_total_qtd * p1_price
        p1_taxful_total = p1_taxless_total * (1 + p1_tr)

        p2_total_qtd = p2_qtd * p2_lines
        p2_taxless_total = p2_total_qtd * p2_price
        p2_taxful_total = p2_taxless_total * (1 + p2_tr)

        if order_by == "quantity":
            p1 = data[0]
            p2 = data[1]

        elif order_by == "taxless_total":
            p1 = data[0]
            p2 = data[1]

        else:    # order_by == "taxful_total":
            p1 = data[1]
            p2 = data[0]

        precision = Decimal('0.1') ** 2

        assert p1["product"] == product1.name
        assert Decimal(p1["quantity"]) == p1_total_qtd
        assert Decimal(p1["taxless_total"]) == p1_taxless_total.quantize(precision)
        assert Decimal(p1["taxful_total"]) == p1_taxful_total.quantize(precision)

        assert p2["product"] == product2.name
        assert Decimal(p2["quantity"]) == p2_total_qtd
        assert Decimal(p2["taxless_total"]) == p2_taxless_total.quantize(precision)
        assert Decimal(p2["taxful_total"]) == p2_taxful_total.quantize(precision)
Exemple #30
0
def test_refunds_for_discounted_order_lines():
    shop = get_default_shop()
    supplier = get_default_supplier()
    product = create_product(
        "test-sku",
        shop=get_default_shop(),
        default_price=10,
    )

    order = create_order_with_product(product, supplier, 2, 200, shop=shop)
    discount_line = OrderLine(
        order_id=order.id, type=OrderLineType.DISCOUNT, quantity=1, discount_amount_value=Decimal("0.54321"))
    discount_line.save()
    order.lines.add(discount_line)

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

    product_line = order.lines.filter(type=OrderLineType.PRODUCT).first()
    product_line.discount_amount = TaxfulPrice(100, order.currency)
    product_line.save()
    taxful_price_with_discount = product_line.taxful_price
    order.cache_prices()
    order.save()

    assert product_line.base_price == TaxfulPrice(400, order.currency)
    assert taxful_price_with_discount == TaxfulPrice(300, order.currency)

    # try to refund only the product line - should fail since this would result in a negative total
    with pytest.raises(RefundExceedsAmountException):
        order.create_refund([{"line": product_line, "quantity": 2, "amount": taxful_price_with_discount.amount}])

    # try to refund the product line with a negative amount
    with pytest.raises(InvalidRefundAmountException):
        order.create_refund([{"line": product_line, "quantity": 1, "amount": -taxful_price_with_discount.amount}])
    # try to refund the discount line with a positive amount
    with pytest.raises(InvalidRefundAmountException):
        order.create_refund([{"line": discount_line, "quantity": 1, "amount": -discount_line.taxful_price.amount}])

    order.create_refund([
        {"line": discount_line, "quantity": 1, "amount": discount_line.taxful_price.amount},
        {"line": product_line, "quantity": 2, "amount": taxful_price_with_discount.amount}
    ])
    assert product_line.max_refundable_amount.value == 0
    assert discount_line.max_refundable_amount.value == 0
    assert order.taxful_total_price.value == 0

    order = create_order_with_product(product, supplier, 2, 200, shop=shop)
    discount_line = OrderLine(
        order_id=order.id, type=OrderLineType.DISCOUNT, quantity=1, discount_amount_value=Decimal("0.54321"))
    discount_line.save()
    order.lines.add(discount_line)
    product_line = order.lines.filter(type=OrderLineType.PRODUCT).first()
    product_line.discount_amount = TaxfulPrice(100, order.currency)
    product_line.save()

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

    order.create_full_refund(restock_products=False)
    assert order.taxful_total_price.value == 0