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