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