示例#1
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(
        product=get_default_product(),
        supplier=get_default_supplier(),
        type=OrderLineType.PRODUCT,
        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")
示例#2
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")
示例#3
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
def test_rounding_costs(price, cost, mode):
    shop = get_default_shop()
    source = OrderSource(shop)
    source.total_price_of_products = source.create_price(price)
    behavior = RoundingBehaviorComponent.objects.create(mode=mode)
    costs = list(behavior.get_costs(None, source))

    assert len(costs) == 1
    assert costs[0].price.value == Decimal(cost)
示例#5
0
    def _initialize_source_from_state(self, state, creator, ip_address, save, order_to_update=None):
        shop_data = state.pop("shop", None).get("selected", {})
        shop = self.safe_get_first(Shop, pk=shop_data.pop("id", None))
        if not shop:
            self.add_error(ValidationError(_("Please choose a valid shop."), code="no_shop"))
            return None

        source = OrderSource(shop=shop)
        if order_to_update:
            source.update_from_order(order_to_update)

        customer_data = state.pop("customer", None)
        billing_address_data = customer_data.pop("billingAddress", {})
        shipping_address_data = (
            billing_address_data
            if customer_data.pop("shipToBillingAddress", False)
            else customer_data.pop("shippingAddress", {}))
        is_company = customer_data.pop("isCompany", False)
        save_address = customer_data.pop("saveAddress", False)
        customer = self._get_customer(customer_data, billing_address_data, is_company, save)
        if not customer:
            return

        billing_address = MutableAddress.from_data(billing_address_data)
        shipping_address = MutableAddress.from_data(shipping_address_data)
        if save:
            billing_address.save()
            shipping_address.save()

        if save and save_address:
            customer.default_billing_address = billing_address
            customer.default_shipping_address = shipping_address
            customer.save()

        methods_data = state.pop("methods", None) or {}
        shipping_method = methods_data.pop("shippingMethod")
        if not shipping_method:
            self.add_error(ValidationError(_("Please select shipping method."), code="no_shipping_method"))

        payment_method = methods_data.pop("paymentMethod")
        if not payment_method:
            self.add_error(ValidationError(_("Please select payment method."), code="no_payment_method"))

        if self.errors:
            return

        source.update(
            creator=creator,
            ip_address=ip_address,
            customer=customer,
            billing_address=billing_address,
            shipping_address=shipping_address,
            status=OrderStatus.objects.get_default_initial(),
            shipping_method=self.safe_get_first(ShippingMethod, pk=shipping_method.get("id")),
            payment_method=self.safe_get_first(PaymentMethod, pk=payment_method.get("id")),
        )
        return source
def test_rounding_costs(price, cost, mode):
    shop = get_default_shop()
    source = OrderSource(shop)
    source.total_price_of_products = source.create_price(price)
    behavior = RoundingBehaviorComponent.objects.create(mode=mode)
    costs = list(behavior.get_costs(None, source))

    assert len(costs) == 1
    assert costs[0].price.value == Decimal(cost)
示例#7
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")
示例#8
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.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
示例#9
0
def test_order_creator_taxes(admin_user, include_tax):
    shop = get_shop(include_tax)
    source = OrderSource(shop)
    source.status = get_initial_order_status()
    create_default_order_statuses()
    tax = get_tax("sales-tax", "Sales Tax", Decimal(0.2))  # 20%
    create_default_tax_rule(tax)
    product = get_default_product()

    line = source.add_line(
        line_id="product-line",
        type=OrderLineType.PRODUCT,
        product=product,
        supplier=get_default_supplier(),
        quantity=1,
        shop=shop,
        base_unit_price=source.create_price(100),
    )
    discount_line = source.add_line(line_id="discount-line",
                                    type=OrderLineType.DISCOUNT,
                                    supplier=get_default_supplier(),
                                    quantity=1,
                                    base_unit_price=source.create_price(0),
                                    discount_amount=source.create_price(100),
                                    parent_line_id=line.line_id)
    assert source.taxful_total_price.value == Decimal()
    creator = OrderCreator()
    order = creator.create_order(source)
    assert order.taxful_total_price.value == Decimal()
示例#10
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
示例#11
0
def test_order_copy_by_updating_order_source_from_order(admin_user):
    shop = get_default_shop()

    line_data = {
        "type": OrderLineType.PRODUCT,
        "product": get_default_product(),
        "supplier": get_default_supplier(),
        "quantity": 1,
        "base_unit_price": shop.create_price(10),
    }
    source = seed_source(admin_user)
    source.add_line(**line_data)
    source.payment_data = None

    creator = OrderCreator()
    order = creator.create_order(source)

    new_source = OrderSource(shop)
    new_source.update_from_order(order)
    new_source.add_line(**line_data)

    new_order = creator.create_order(new_source)
    assert new_order
    assert order.billing_address == new_order.billing_address
    assert order.taxful_total_price == new_order.taxful_total_price
示例#12
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
示例#13
0
def create_order_source(prices_include_tax, line_data, tax_rates):
    """
    Get order source with some testing data.

    :rtype: OrderSource
    """
    lines = [Line.from_text(x) for x in line_data]
    shop = get_shop(prices_include_tax, currency='USD')
    tax_classes = create_assigned_tax_classes(tax_rates)
    products = create_products(shop, lines, tax_classes)
    services = create_services(shop, lines, tax_classes)

    source = OrderSource(shop)
    fill_order_source(source, lines, products, services)
    return source
示例#14
0
def test_product_catalog_indexing(rf, admin_user, settings):
    shop = get_default_shop()
    supplier = get_simple_supplier(shop=shop)
    supplier.stock_managed = True
    supplier.save()
    product = create_product("simple-test-product", shop, supplier)

    ProductCatalog.index_product(product)

    # no purchasable products
    catalog = ProductCatalog(
        ProductCatalogContext(shop=shop, purchasable_only=True))
    assert catalog.get_products_queryset().count() == 0

    # add 10 items to the stock
    stock_qty = 10
    request = apply_request_middleware(rf.post("/",
                                               data={
                                                   "purchase_price":
                                                   decimal.Decimal(32.00),
                                                   "delta":
                                                   stock_qty
                                               }),
                                       user=admin_user)
    response = process_stock_adjustment(request, supplier.id, product.id)
    assert response.status_code == 200
    pss = supplier.get_stock_status(product.pk)
    assert pss.logical_count == stock_qty

    # now there are purchasable products
    assert catalog.get_products_queryset().count() == 1
    assert product in catalog.get_products_queryset()

    # create a random order with 10 units of the product
    source = OrderSource(shop)
    source.status = get_initial_order_status()
    source.add_line(
        type=OrderLineType.PRODUCT,
        supplier=supplier,
        product=product,
        base_unit_price=source.create_price(1),
        quantity=10,
    )
    OrderCreator().create_order(source)

    pss = supplier.get_stock_status(product.pk)
    assert pss.logical_count == 0

    # stocks are gone
    assert catalog.get_products_queryset().count() == 0
示例#15
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
示例#16
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.list_visible(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
示例#17
0
def test_codes_type_conversion():
    source = OrderSource(Shop())

    assert source.codes == []

    source.add_code("t")
    source.add_code("e")
    source.add_code("s")
    assert source.codes == ["t", "e", "s"]

    was_added = source.add_code("t")
    assert was_added is False
    assert source.codes == ["t", "e", "s"]

    was_cleared = source.clear_codes()
    assert was_cleared
    assert source.codes == []
    was_cleared = source.clear_codes()
    assert not was_cleared

    source.add_code("test")
    source.add_code(1)
    source.add_code("1")
    assert source.codes == ["test", "1"]
示例#18
0
def test_invalid_source_line_updating():
    source = OrderSource(Shop())
    with pytest.raises(TypeError):  # Test forbidden keys
        SourceLine(source).update({"update": True})
示例#19
0
def test_invalid_order_source_updating():
    with pytest.raises(ValueError):  # Test nonexisting key updating
        OrderSource(Shop()).update(__totes_not_here__=True)
示例#20
0
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.status = OrderStatus.objects.get_default_complete()
            order.save(update_fields=("status",))
        return order
示例#21
0
def _get_source_line(request):
    source = OrderSource(request.shop)
    return _create_line(source, Product(sku="6.0745"))
示例#22
0
def test_codes_type_conversion():
    source = OrderSource(Shop())

    assert source.codes == []

    source.add_code("t")
    source.add_code("e")
    source.add_code("s")
    assert source.codes == ["t", "e", "s"]

    was_added = source.add_code("t")
    assert was_added is False
    assert source.codes == ["t", "e", "s"]

    was_cleared = source.clear_codes()
    assert was_cleared
    assert source.codes == []
    was_cleared = source.clear_codes()
    assert not was_cleared

    source.add_code("test")
    source.add_code(1)
    source.add_code("1")
    assert source.codes == ["test", "1"]
示例#23
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
示例#24
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)
示例#25
0
    def _initialize_source_from_state(self,
                                      state,
                                      creator,
                                      ip_address,
                                      save,
                                      order_to_update=None):
        shop_data = state.pop("shop", None).get("selected", {})
        shop = self.safe_get_first(Shop, pk=shop_data.pop("id", None))
        if not shop:
            self.add_error(
                ValidationError(_("Please choose a valid shop."),
                                code="no_shop"))
            return None

        source = OrderSource(shop=shop)
        if order_to_update:
            source.update_from_order(order_to_update)

        customer_data = state.pop("customer", None)
        billing_address_data = customer_data.pop("billingAddress", {})
        shipping_address_data = (billing_address_data if customer_data.pop(
            "shipToBillingAddress", False) else customer_data.pop(
                "shippingAddress", {}))
        is_company = customer_data.pop("isCompany", False)
        save_address = customer_data.pop("saveAddress", False)
        customer = self._get_customer(customer_data, billing_address_data,
                                      is_company, save)
        if not customer:
            return

        billing_address = MutableAddress.from_data(billing_address_data)
        shipping_address = MutableAddress.from_data(shipping_address_data)
        if save:
            billing_address.save()
            shipping_address.save()

        if save and save_address:
            customer.default_billing_address = billing_address
            customer.default_shipping_address = shipping_address
            customer.save()

        methods_data = state.pop("methods", None) or {}
        shipping_method = methods_data.pop("shippingMethod")
        if not shipping_method:
            self.add_error(
                ValidationError(_("Please select shipping method."),
                                code="no_shipping_method"))

        payment_method = methods_data.pop("paymentMethod")
        if not payment_method:
            self.add_error(
                ValidationError(_("Please select payment method."),
                                code="no_payment_method"))

        if self.errors:
            return

        source.update(
            creator=creator,
            ip_address=ip_address,
            customer=customer,
            billing_address=billing_address,
            shipping_address=shipping_address,
            status=OrderStatus.objects.get_default_initial(),
            shipping_method=self.safe_get_first(ShippingMethod,
                                                pk=shipping_method.get("id")),
            payment_method=self.safe_get_first(PaymentMethod,
                                               pk=payment_method.get("id")),
        )
        return source