예제 #1
0
def test_stacked_tax_taxful_price():
    shop = get_shop(prices_include_tax=True, currency='EUR')
    source = OrderSource(shop)
    assert source.prices_include_tax
    source.add_line(
        type=OrderLineType.OTHER, quantity=1, base_unit_price=source.create_price(20)
    )
    with override_provides("tax_module", TAX_MODULE_SPEC):
        with override_settings(SHUUP_TAX_MODULE="irvine"):
            source.shipping_address = MutableAddress(
                street="16215 Alton Pkwy",
                postal_code="92602",
            )
            line = source.get_final_lines(with_taxes=True)[0]
            assert isinstance(line, SourceLine)
            assert line.taxes
            assert line.taxful_price == TaxfulPrice(20, 'EUR')
            assert_almost_equal(line.taxless_price, TaxlessPrice("18.52", 'EUR'))
            source.uncache()

            # Let's move out to a taxless location.
            source.shipping_address.postal_code = "11111"
            line = source.get_final_lines(with_taxes=True)[0]
            assert isinstance(line, SourceLine)
            assert not line.taxes
            assert line.taxful_price == TaxfulPrice(20, source.currency)
            assert line.taxless_price.value == Decimal("20")
예제 #2
0
def test_processor_orderability(admin_user):
    source = OrderSource(Shop())
    processor = OrderProcessor()
    line = source.add_line(
        type=OrderLineType.PRODUCT,
        product=get_default_product(),
        supplier=get_default_supplier(),
        quantity=1,
        shop=get_default_shop(),
        base_unit_price=source.create_price(10),
    )
    line.order = Order(shop=get_default_shop())
    assert processor._check_orderability(line) is None

    unorderable_line = source.add_line(
        type=OrderLineType.PRODUCT,
        product=create_product("no-shop"),
        supplier=get_default_supplier(),
        quantity=1,
        shop=get_default_shop(),
        base_unit_price=source.create_price(20),
    )
    unorderable_line.order = Order(shop=get_default_shop())
    with pytest.raises(ValidationError) as exc:
        processor._check_orderability(unorderable_line)
    assert "Not available in" in exc.value.message
예제 #3
0
def create_random_order(customer=None, products=(), completion_probability=0, shop=None):
    if not customer:
        customer = Contact.objects.all().order_by("?").first()

    if not customer:
        raise ValueError("No valid contacts")

    if shop is None:
        shop = get_default_shop()

    pricing_context = _get_pricing_context(shop, customer)

    source = OrderSource(shop)
    source.customer = customer
    source.customer_comment = "Mock Order"

    if customer.default_billing_address and customer.default_shipping_address:
        source.billing_address = customer.default_billing_address
        source.shipping_address = customer.default_shipping_address
    else:
        source.billing_address = create_random_address()
        source.shipping_address = create_random_address()
    source.order_date = now() - datetime.timedelta(days=random.uniform(0, 400))

    source.language = customer.language
    source.status = get_initial_order_status()

    if not products:
        products = list(Product.objects.listed(source.shop, customer).order_by("?")[:40])

    for i in range(random.randint(3, 10)):
        product = random.choice(products)
        quantity = random.randint(1, 5)
        price_info = product.get_price_info(pricing_context, quantity=quantity)
        shop_product = product.get_shop_instance(source.shop)
        supplier = shop_product.suppliers.first()
        line = source.add_line(
            type=OrderLineType.PRODUCT,
            product=product,
            supplier=supplier,
            quantity=quantity,
            base_unit_price=price_info.base_unit_price,
            discount_amount=price_info.discount_amount,
            sku=product.sku,
            text=product.safe_translation_getter("name", any_language=True)
        )
        assert line.price == price_info.price
    with atomic():
        oc = OrderCreator()
        order = oc.create_order(source)
        if random.random() < completion_probability:
            order.create_shipment_of_all_products()
            # also set complete
            order.status = OrderStatus.objects.get_default_complete()
            order.save(update_fields=("status",))
        return order
def create_random_order(customer=None, products=(), completion_probability=0, shop=None):
    if not customer:
        customer = Contact.objects.all().order_by("?").first()

    if not customer:
        raise ValueError("No valid contacts")

    if shop is None:
        shop = get_default_shop()

    pricing_context = _get_pricing_context(shop, customer)

    source = OrderSource(shop)
    source.customer = customer
    source.customer_comment = "Mock Order"

    if customer.default_billing_address and customer.default_shipping_address:
        source.billing_address = customer.default_billing_address
        source.shipping_address = customer.default_shipping_address
    else:
        source.billing_address = create_random_address()
        source.shipping_address = create_random_address()
    source.order_date = now() - datetime.timedelta(days=random.uniform(0, 400))

    source.status = get_initial_order_status()

    if not products:
        products = list(Product.objects.listed(source.shop, customer).order_by("?")[:40])

    for i in range(random.randint(3, 10)):
        product = random.choice(products)
        quantity = random.randint(1, 5)
        price_info = product.get_price_info(pricing_context, quantity=quantity)
        shop_product = product.get_shop_instance(source.shop)
        supplier = shop_product.suppliers.first()
        line = source.add_line(
            type=OrderLineType.PRODUCT,
            product=product,
            supplier=supplier,
            quantity=quantity,
            base_unit_price=price_info.base_unit_price,
            discount_amount=price_info.discount_amount,
            sku=product.sku,
            text=product.safe_translation_getter("name", any_language=True)
        )
        assert line.price == price_info.price
    with atomic():
        oc = OrderCreator()
        order = oc.create_order(source)
        if random.random() < completion_probability:
            order.create_shipment_of_all_products()
            # also set complete
            order.status = OrderStatus.objects.get_default_complete()
            order.save(update_fields=("status",))
        return order
예제 #5
0
def test_stacked_tax_taxless_price():
    source = OrderSource(get_shop(prices_include_tax=False))
    assert source.prices_include_tax is False
    source.add_line(type=OrderLineType.OTHER,
                    quantity=1,
                    base_unit_price=source.create_price(10))
    with override_provides("tax_module", TAX_MODULE_SPEC):
        with override_settings(SHUUP_TAX_MODULE="irvine"):
            source.shipping_address = MutableAddress(
                street="16215 Alton Pkwy",
                postal_code="92602",
            )
            line = source.get_final_lines(with_taxes=True)[0]
            assert isinstance(line, SourceLine)
            assert line.taxes
            assert line.taxful_price.value == Decimal("10.800")
            source.uncache()

            # Let's move out to a taxless location.
            source.shipping_address.postal_code = "11111"
            line = source.get_final_lines(with_taxes=True)[0]
            assert isinstance(line, SourceLine)
            assert not line.taxes
            assert line.taxful_price.value == Decimal("10")
예제 #6
0
def test_order_creator_orderability(admin_user):
    source = OrderSource(get_default_shop())
    product = get_default_product()

    line = source.add_line(
        type=OrderLineType.PRODUCT,
        product=product,
        supplier=get_default_supplier(),
        quantity=1,
        shop=get_default_shop(),
        base_unit_price=source.create_price(10),
    )
    assert len(list(source.get_validation_errors())) == 0

    # delete the shop product
    product.get_shop_instance(get_default_shop()).delete()

    errors = list(source.get_validation_errors())
    assert len(errors) == 1
    assert "product_not_available_in_shop" in errors[0].code
예제 #7
0
def test_order_creator_orderability(admin_user):
    source = OrderSource(get_default_shop())
    product = get_default_product()

    line = source.add_line(
        type=OrderLineType.PRODUCT,
        product=product,
        supplier=get_default_supplier(),
        quantity=1,
        shop=get_default_shop(),
        base_unit_price=source.create_price(10),
    )
    assert len(list(source.get_validation_errors())) == 0

    # delete the shop product
    product.get_shop_instance(get_default_shop()).delete()

    errors = list(source.get_validation_errors())
    assert len(errors) == 1
    assert "product_not_available_in_shop" in errors[0].code
예제 #8
0
def test_order_full_refund_with_taxes(include_tax):
    tax_rate = Decimal(0.2)  # 20%
    product_price = 100
    discount_amount = 30
    random_line_price = 5

    shop = factories.get_shop(include_tax)
    source = OrderSource(shop)
    source.status = factories.get_initial_order_status()
    supplier = factories.get_default_supplier()
    create_default_order_statuses()
    tax = factories.get_tax("sales-tax", "Sales Tax", tax_rate)
    factories.create_default_tax_rule(tax)

    product = factories.create_product("sku",
                                       shop=shop,
                                       supplier=supplier,
                                       default_price=product_price)

    line = source.add_line(
        line_id="product-line",
        type=OrderLineType.PRODUCT,
        product=product,
        supplier=supplier,
        quantity=1,
        shop=shop,
        base_unit_price=source.create_price(product_price),
    )
    discount_line = source.add_line(
        line_id="discount-line",
        type=OrderLineType.DISCOUNT,
        supplier=supplier,
        quantity=1,
        base_unit_price=source.create_price(0),
        discount_amount=source.create_price(discount_amount),
        parent_line_id=line.line_id)
    raw_total_price = Decimal(product_price - discount_amount)
    total_taxful = bround(source.taxful_total_price.value)
    total_taxless = bround(source.taxless_total_price.value)
    if include_tax:
        assert total_taxful == bround(raw_total_price)
        assert total_taxless == bround(raw_total_price / (1 + tax_rate))
    else:
        assert total_taxful == bround(raw_total_price * (1 + tax_rate))
        assert total_taxless == bround(raw_total_price)

    # Lines without quantity shouldn't affect refunds
    other_line = source.add_line(
        text="This random line for textual information",
        line_id="other-line",
        type=OrderLineType.OTHER,
        quantity=0)
    # Lines with quantity again should be able to be refunded normally.
    other_line_with_quantity = source.add_line(
        line_id="other_line_with_quantity",
        type=OrderLineType.OTHER,
        text="Special service $5/h",
        quantity=1,
        base_unit_price=source.create_price(random_line_price))

    raw_total_price = Decimal(product_price - discount_amount +
                              random_line_price)
    total_taxful = bround(source.taxful_total_price.value)
    total_taxless = bround(source.taxless_total_price.value)
    if include_tax:
        assert total_taxful == bround(raw_total_price)
        assert total_taxless == bround(raw_total_price / (1 + tax_rate))
    else:
        assert total_taxful == bround(raw_total_price * (1 + tax_rate))
        assert total_taxless == bround(raw_total_price)

    creator = OrderCreator()
    order = creator.create_order(source)
    assert order.taxful_total_price.value == total_taxful
    assert order.taxless_total_price.value == total_taxless

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

    order.create_full_refund()
    assert order.taxful_total_price_value == 0

    for parent_order_line in order.lines.filter(parent_line__isnull=True):
        if parent_order_line.quantity == 0:
            assert not parent_order_line.child_lines.exists()
        else:
            refund_line = parent_order_line.child_lines.filter(
                type=OrderLineType.REFUND).first()
            assert refund_line
            assert parent_order_line.taxful_price.value == -refund_line.taxful_price.value
            assert parent_order_line.taxless_price.value == -refund_line.taxless_price.value
            assert parent_order_line.price.value == -refund_line.price.value
예제 #9
0
def test_order_partial_refund_with_taxes(include_tax):
    tax_rate = Decimal(0.2)  # 20%
    product_price = 100
    discount_amount = 30
    random_line_price = 5
    refunded_amount = 15

    shop = factories.get_shop(include_tax)
    source = OrderSource(shop)
    source.status = factories.get_initial_order_status()
    supplier = factories.get_default_supplier()
    create_default_order_statuses()
    tax = factories.get_tax("sales-tax", "Sales Tax", tax_rate)
    factories.create_default_tax_rule(tax)

    product = factories.create_product("sku",
                                       shop=shop,
                                       supplier=supplier,
                                       default_price=product_price)

    line = source.add_line(
        line_id="product-line",
        type=OrderLineType.PRODUCT,
        product=product,
        supplier=supplier,
        quantity=1,
        shop=shop,
        base_unit_price=source.create_price(product_price),
    )
    discount_line = source.add_line(
        line_id="discount-line",
        type=OrderLineType.DISCOUNT,
        supplier=supplier,
        quantity=1,
        base_unit_price=source.create_price(0),
        discount_amount=source.create_price(discount_amount),
        parent_line_id=line.line_id)
    raw_total_price = Decimal(product_price - discount_amount)
    total_taxful = bround(source.taxful_total_price.value)
    total_taxless = bround(source.taxless_total_price.value)
    if include_tax:
        assert total_taxful == bround(raw_total_price)
        assert total_taxless == bround(raw_total_price / (1 + tax_rate))
    else:
        assert total_taxful == bround(raw_total_price * (1 + tax_rate))
        assert total_taxless == bround(raw_total_price)

    creator = OrderCreator()
    order = creator.create_order(source)
    assert order.taxful_total_price.value == total_taxful
    assert order.taxless_total_price.value == total_taxless

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

    refund_data = [
        dict(
            amount=Money(refunded_amount, shop.currency),
            quantity=1,
            line=order.lines.products().first(),
        )
    ]
    order.create_refund(refund_data)

    total_taxful = bround(order.taxful_total_price.value)
    total_taxless = bround(order.taxless_total_price.value)
    taxless_refunded_amount = (refunded_amount / (1 + tax_rate))

    if include_tax:
        raw_total_price = Decimal(product_price - discount_amount -
                                  refunded_amount)
        assert total_taxful == bround(raw_total_price)
        assert total_taxless == bround(raw_total_price / (1 + tax_rate))
    else:
        # the refunded amount it considered a taxful price internally
        raw_total_price = Decimal(product_price - discount_amount)
        assert total_taxful == bround((raw_total_price * (1 + tax_rate)) -
                                      refunded_amount)
        assert total_taxless == bround(raw_total_price -
                                       taxless_refunded_amount)

    refund_line = order.lines.refunds().filter(
        type=OrderLineType.REFUND).first()
    if include_tax:
        assert refund_line.taxful_price.value == -bround(refunded_amount)
        assert refund_line.taxless_price.value == -bround(
            taxless_refunded_amount)
    else:
        assert refund_line.taxful_price.value == -bround(refunded_amount)
        assert refund_line.taxless_price.value == -bround(
            taxless_refunded_amount)
예제 #10
0
파일: factories.py 프로젝트: rrosajp/shuup
def create_random_order(  # noqa
    customer=None,
    products=(),
    completion_probability=0,
    shop=None,
    random_products=True,
    create_payment_for_order_total=False,
    order_date=None,
):
    if not customer:
        customer = Contact.objects.all().order_by("?").first()

    if not customer:
        raise ValueError("Error! No valid contacts.")

    if shop is None:
        shop = get_default_shop()

    pricing_context = _get_pricing_context(shop, customer)

    source = OrderSource(shop)
    source.customer = customer
    source.customer_comment = "Mock Order"

    if customer.default_billing_address and customer.default_shipping_address:
        source.billing_address = customer.default_billing_address
        source.shipping_address = customer.default_shipping_address
    else:
        source.billing_address = create_random_address()
        source.shipping_address = create_random_address()
    source.order_date = order_date or (now() - datetime.timedelta(days=random.uniform(0, 400)))

    source.status = get_initial_order_status()

    if not products:
        products = list(Product.objects.listed(source.shop, customer).order_by("?")[:40])

    if random_products:
        quantity = random.randint(3, 10)
    else:
        quantity = len(products)

    for i in range(quantity):
        if random_products:
            product = random.choice(products)
        else:
            product = products[i]

        quantity = random.randint(1, 5)
        price_info = product.get_price_info(pricing_context, quantity=quantity)
        shop_product = product.get_shop_instance(source.shop)
        supplier = shop_product.get_supplier(source.customer, quantity, source.shipping_address)
        line = source.add_line(
            type=OrderLineType.PRODUCT,
            product=product,
            supplier=supplier,
            quantity=quantity,
            base_unit_price=price_info.base_unit_price,
            discount_amount=price_info.discount_amount,
            sku=product.sku,
            text=product.safe_translation_getter("name", any_language=True),
        )
        assert line.price == price_info.price

    with atomic():
        oc = OrderCreator()
        order = oc.create_order(source)
        if random.random() < completion_probability:
            suppliers = set([line.supplier for line in order.lines.filter(supplier__isnull=False, quantity__gt=0)])
            for supplier in suppliers:
                order.create_shipment_of_all_products(supplier=supplier)

            if create_payment_for_order_total:
                order.create_payment(order.taxful_total_price)

            # also set complete
            order.save()
            order.change_status(next_status=get_completed_order_status(), user=customer.user)
        return order