def test_order_creation_adds_usage(rf, admin_user): request, shop, group = initialize_test(rf, False) source = seed_source(admin_user, shop) source.add_line( type=OrderLineType.PRODUCT, product=get_default_product(), supplier=get_default_supplier(shop), quantity=1, base_unit_price=source.create_price(10), ) source.add_line( type=OrderLineType.OTHER, quantity=1, base_unit_price=source.create_price(10), require_verification=True, ) # add coupon coupon = Coupon.objects.create(active=True, code="asdf") campaign = BasketCampaign.objects.create(active=True, shop=shop, name="test", public_name="test", coupon=coupon) BasketDiscountPercentage.objects.create(campaign=campaign, discount_percentage="0.1") source.add_code(coupon.code) creator = OrderCreator() creator.create_order(source) assert CouponUsage.objects.count() == 1
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
def test_order_creator_account_manager(): company = create_random_company() shop = get_shop(identifier="random-shop", enabled=True) source = seed_source(create_random_user(), shop) source.customer = company source.add_line( type=OrderLineType.PRODUCT, product=get_default_product(), supplier=get_default_supplier(), quantity=1, base_unit_price=source.create_price(10), ) creator = OrderCreator() order = creator.create_order(source) assert order.account_manager is None # Company contact doesn't have account manager field person = create_random_person() person.account_manager = create_random_person() person.save() source = seed_source(create_random_user(), shop) source.customer = person source.add_line( type=OrderLineType.PRODUCT, product=get_default_product(), supplier=get_default_supplier(), quantity=1, base_unit_price=source.create_price(10), ) creator = OrderCreator() order = creator.create_order(source) assert order.account_manager is not None assert order.account_manager == person.account_manager with pytest.raises(ProtectedError): person.account_manager.delete()
def test_order_creation_adds_usage(rf, admin_user): request, shop, group = initialize_test(rf, False) source = seed_source(admin_user) source.add_line( type=OrderLineType.PRODUCT, product=get_default_product(), supplier=get_default_supplier(), quantity=1, base_unit_price=source.create_price(10), ) source.add_line( type=OrderLineType.OTHER, quantity=1, base_unit_price=source.create_price(10), require_verification=True, ) # add coupon coupon = Coupon.objects.create(active=True, code="asdf") campaign = BasketCampaign.objects.create( active=True, shop=shop, name="test", public_name="test", coupon=coupon) BasketDiscountPercentage.objects.create(campaign=campaign, discount_percentage="0.1") source.add_code(coupon.code) creator = OrderCreator() creator.create_order(source) assert CouponUsage.objects.count() == 1
def test_refunds_report(rf): shop = get_default_shop() get_default_tax_class() creator = OrderCreator() source1 = seed_source() source2 = seed_source() source3 = seed_source() source4 = seed_source() order1 = creator.create_order(source1) order2 = creator.create_order(source2) order3 = creator.create_order(source3) order4 = creator.create_order(source4) # pay orders [o.create_payment(o.taxful_total_price) for o in Order.objects.all()] order1.create_full_refund() order2.create_refund( [{"line": order2.lines.first(), "amount": order2.taxful_total_price.amount * Decimal(0.5), "quantity": 1}] ) order3.create_refund( [{"line": order3.lines.first(), "amount": order3.taxful_total_price.amount * Decimal(0.3), "quantity": 1}] ) order4.create_refund( [{"line": order4.lines.first(), "amount": order4.taxful_total_price.amount * Decimal(0.1), "quantity": 1}] ) total_refunded = (order1.get_total_refunded_amount() + order2.get_total_refunded_amount() + order3.get_total_refunded_amount() + order4.get_total_refunded_amount()) data = { "report": RefundedSalesReport.get_name(), "shop": shop.pk, "date_range": DateRangeChoices.ALL_TIME, "writer": "json", "force_download": 1, } report = RefundedSalesReport(**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(RefundedSalesReport.title) in json_data.get("heading") data = json_data.get("tables")[0].get("data") assert len(data) == 1 data = data[0] expected_data = { "refunded_orders": "4", "total_refunded": str(total_refunded.value) } for k, v in expected_data.items(): assert data[k] == v
def test_order_creator_with_package_product(rf, admin_user): if "shuup.simple_supplier" not in settings.INSTALLED_APPS: pytest.skip("Need shuup.simple_supplier in INSTALLED_APPS") from shuup_tests.simple_supplier.utils import get_simple_supplier shop = get_default_shop() supplier = get_simple_supplier() package_product = create_package_product("Package-Product-Test", shop=shop, supplier=supplier, children=2) shop_product = package_product.get_shop_instance(shop) quantity_map = package_product.get_package_child_to_quantity_map() product_1, product_2 = quantity_map.keys() product_1.stock_behavior = StockBehavior.STOCKED product_1.save() product_2.stock_behavior = StockBehavior.STOCKED product_2.save() assert quantity_map[product_1] == 1 assert quantity_map[product_2] == 2 supplier.adjust_stock(product_1.pk, 1) supplier.adjust_stock(product_2.pk, 2) assert supplier.get_stock_status(product_1.pk).logical_count == 1 assert supplier.get_stock_status(product_2.pk).logical_count == 2 creator = OrderCreator() # There should be no exception when creating order with only package product source = seed_source(admin_user) source.add_line( type=OrderLineType.PRODUCT, product=package_product, supplier=supplier, quantity=1, base_unit_price=source.create_price(10), ) order = creator.create_order(source) # However, there should not be enough stock for both package and child products source = seed_source(admin_user) source.add_line( type=OrderLineType.PRODUCT, product=package_product, supplier=supplier, quantity=1, base_unit_price=source.create_price(10), ) source.add_line( type=OrderLineType.PRODUCT, product=product_1, supplier=supplier, quantity=1, base_unit_price=source.create_price(10), ) # And a validation error should be raised with pytest.raises(ValidationError): order = creator.create_order(source)
def _create_order(request, customer, coupon, product, expected_product_price): creator = OrderCreator(request) shop = request.shop request.basket = None request.customer = customer basket = get_basket(request) basket.status = factories.get_initial_order_status() basket.add_product(supplier=factories.get_default_supplier(), shop=shop, product=product, quantity=1) basket.shipping_method = factories.get_shipping_method(shop=shop) basket.payment_method = factories.get_payment_method(shop=shop) basket.add_code(coupon) assert basket.shop == request.shop assert basket.customer == request.customer assert product.get_price_info(request).price == expected_product_price creator.create_order(basket)
def test_order_source_extra_data(rf, admin_user): source = seed_source(admin_user) product = get_default_product() line1 = source.add_line( type=OrderLineType.PRODUCT, product=product, supplier=get_default_supplier(), quantity=1, base_unit_price=source.create_price(10), line_id="parent" ) line2 = source.add_line( type=OrderLineType.OTHER, text="Child Line", sku="KIDKIDKID", quantity=1, base_unit_price=source.create_price(5), parent_line_id="parent" ) creator = OrderCreator() order = Order.objects.get(pk=creator.create_order(source).pk) line_ids = [line.extra_data["source_line_id"] for line in order.lines.all()] assert line1.line_id in line_ids assert line2.line_id in line_ids
def get_order_and_source(admin_user, product): # create original source to tamper with source = BasketishOrderSource(get_default_shop()) source.status = get_initial_order_status() source.billing_address = MutableAddress.objects.create(name="Original Billing") source.shipping_address = MutableAddress.objects.create(name="Original Shipping") source.customer = get_person_contact(admin_user) source.payment_method = get_default_payment_method() source.shipping_method = get_default_shipping_method() source.add_line( type=OrderLineType.PRODUCT, product=product, supplier=get_default_supplier(), quantity=1, base_unit_price=source.create_price(10), ) source.add_line( type=OrderLineType.OTHER, quantity=1, base_unit_price=source.create_price(10), require_verification=True, ) assert len(source.get_lines()) == 2 source.creator = admin_user creator = OrderCreator() order = creator.create_order(source) return order, source
def create_order_from_state(self, state, creator=None, ip_address=None): """ Create an order from a state dict unserialized from JSON. :param state: State dictionary :type state: dict :param creator: Creator user :type creator: django.contrib.auth.models.User|None :param ip_address: Remote IP address (IPv4 or IPv6) :type ip_address: str :return: The created order, or None if something failed along the way :rtype: Order|None """ source = self.create_source_from_state(state, creator=creator, ip_address=ip_address, save=True) # Then create an OrderCreator and try to get things done! creator = OrderCreator() try: order = creator.create_order(order_source=source) self._postprocess_order(order, state) return order except Exception as exc: # pragma: no cover self.add_error(exc) return
def test_percentage_campaign_full_discount(rf, include_tax): request, shop, group = initialize_test(rf, include_tax) create_default_order_statuses() tax = get_tax("sales-tax", "Sales Tax", Decimal(0.2)) # 20% create_default_tax_rule(tax) basket = get_basket(request) supplier = get_default_supplier() product = create_product(printable_gibberish(), shop=shop, supplier=supplier, default_price=200) basket.add_product(supplier=supplier, shop=shop, product=product, quantity=1) basket.shipping_method = get_shipping_method(shop=shop) basket.status = get_initial_order_status() campaign = BasketCampaign.objects.create(shop=shop, public_name="test", name="test", active=True) # 100% of discount BasketDiscountPercentage.objects.create(campaign=campaign, discount_percentage=Decimal(1)) assert len(basket.get_final_lines()) == 3 assert basket.product_count == 1 assert basket.total_price.value == Decimal() order_creator = OrderCreator() order = order_creator.create_order(basket) order.create_payment(order.taxful_total_price) 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 test_campaign_with_coupons1(rf): basket, dc, request, status = _init_basket_coupon_test(rf) assert len(basket.get_final_lines()) == 3 # no discount was applied because coupon is required basket.add_code(dc.code) assert len(basket.get_final_lines()) == 4 # now basket has codes so they will be applied too assert OrderLineType.DISCOUNT in [l.type for l in basket.get_final_lines()] # Ensure codes persist between requests, so do what the middleware would, i.e. basket.save() # and then reload the basket: del request.basket basket = get_basket(request) assert basket.codes == [dc.code] assert len(basket.get_final_lines()) == 3 # now basket has codes so they will be applied too assert OrderLineType.DISCOUNT in [l.type for l in basket.get_final_lines()] basket.status = status creator = OrderCreator(request) order = creator.create_order(basket) assert CouponUsage.objects.filter(order=order).count() == 1 assert CouponUsage.objects.filter(order=order, coupon__code=dc.code).count() == 1
def test_order_creator(rf, admin_user): source = seed_source(admin_user) source.add_line( type=OrderLineType.PRODUCT, product=get_default_product(), supplier=get_default_supplier(), quantity=1, base_unit_price=source.create_price(10), ) source.add_line( type=OrderLineType.OTHER, quantity=1, base_unit_price=source.create_price(10), require_verification=True, ) creator = OrderCreator() order = creator.create_order(source) assert get_data_dict(source.billing_address) == get_data_dict(order.billing_address) assert get_data_dict(source.shipping_address) == get_data_dict(order.shipping_address) customer = source.customer assert customer == order.customer assert customer.groups.count() == 1 assert customer.groups.first() == order.customer_groups.first() assert customer.tax_group is not None assert customer.tax_group == order.tax_group assert source.payment_method == order.payment_method assert source.shipping_method == order.shipping_method assert order.pk
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 test_order_statuses(admin_user): create_default_order_statuses() source = seed_source(admin_user) source.add_line( type=OrderLineType.PRODUCT, product=get_default_product(), supplier=get_default_supplier(), quantity=1, base_unit_price=source.create_price(10), ) source.add_line( type=OrderLineType.OTHER, quantity=1, base_unit_price=source.create_price(10), require_verification=True, ) creator = OrderCreator() order = creator.create_order(source) # new order, status/role is new/initial assert order.status.identifier == DefaultOrderStatus.INITIAL.value assert order.status.role == OrderStatusRole.INITIAL # FUTURE: order gets payment the status changes to processing/processing total = order.taxful_total_price.amount order.create_payment(total) assert order.status.identifier == DefaultOrderStatus.INITIAL.value assert order.status.role == OrderStatusRole.INITIAL # FUTURE: order is fully shipped the status changes to complete/complete order.create_shipment_of_all_products() assert order.status.identifier == DefaultOrderStatus.INITIAL.value assert order.status.role == OrderStatusRole.INITIAL
def _get_order_with_coupon(request, initial_status, condition_product_count=1): shop = request.shop basket = get_basket(request) supplier = get_default_supplier() product = create_product(printable_gibberish(), shop=shop, supplier=supplier, default_price="50") basket.add_product(supplier=supplier, shop=shop, product=product, quantity=1) basket.shipping_method = get_shipping_method(shop=shop) # For shippable products dc = Coupon.objects.create(code="TEST", active=True) campaign = BasketCampaign.objects.create( shop=shop, name="test", public_name="test", coupon=dc, active=True ) BasketDiscountAmount.objects.create(discount_amount=shop.create_price("20"), campaign=campaign) rule = BasketTotalProductAmountCondition.objects.create(value=1) campaign.conditions.add(rule) campaign.save() basket.add_code(dc.code) basket.save() basket.status = initial_status creator = OrderCreator(request) order = creator.create_order(basket) assert order.lines.count() == 3 assert OrderLineType.DISCOUNT in [l.type for l in order.lines.all()] return order
def create_order_from_state(self, state, creator=None, ip_address=None): """ Create an order from a state dict unserialized from JSON. :param state: State dictionary :type state: dict :param creator: Creator user :type creator: django.contrib.auth.models.User|None :param ip_address: Remote IP address (IPv4 or IPv6) :type ip_address: str :return: The created order, or None if something failed along the way :rtype: Order|None """ source = self.create_source_from_state( state, creator=creator, ip_address=ip_address, save=True) # Then create an OrderCreator and try to get things done! creator = OrderCreator() try: order = creator.create_order(order_source=source) self._postprocess_order(order, state) return order except Exception as exc: # pragma: no cover self.add_error(exc) return
def test_campaign_with_coupons1(rf): basket, dc, request, status = _init_basket_coupon_test(rf) assert len(basket.get_final_lines() ) == 3 # no discount was applied because coupon is required basket.add_code(dc.code) assert len(basket.get_final_lines() ) == 4 # now basket has codes so they will be applied too assert OrderLineType.DISCOUNT in [l.type for l in basket.get_final_lines()] # Ensure codes persist between requests, so do what the middleware would, i.e. basket.save() # and then reload the basket: del request.basket basket = get_basket(request) assert basket.codes == [dc.code] assert len(basket.get_final_lines() ) == 4 # now basket has codes so they will be applied too assert OrderLineType.DISCOUNT in [l.type for l in basket.get_final_lines()] basket.status = status creator = OrderCreator(request) order = creator.create_order(basket) assert CouponUsage.objects.filter(order=order).count() == 1 assert CouponUsage.objects.filter(order=order, coupon__code=dc.code).count() == 1
def test_order_creator(rf, admin_user): source = seed_source(admin_user) source.add_line( type=OrderLineType.PRODUCT, product=get_default_product(), supplier=get_default_supplier(), quantity=1, base_unit_price=source.create_price(10), ) source.add_line( type=OrderLineType.OTHER, quantity=1, base_unit_price=source.create_price(10), require_verification=True, ) creator = OrderCreator() order = creator.create_order(source) assert get_data_dict(source.billing_address) == get_data_dict( order.billing_address) assert get_data_dict(source.shipping_address) == get_data_dict( order.shipping_address) customer = source.customer assert customer == order.customer assert customer.groups.count() == 1 assert customer.groups.first() == order.customer_groups.first() assert customer.tax_group is not None assert customer.tax_group == order.tax_group assert source.payment_method == order.payment_method assert source.shipping_method == order.shipping_method assert order.pk
def get_order_and_source(admin_user): # create original source to tamper with source = BasketishOrderSource(get_default_shop()) source.status = get_initial_order_status() source.billing_address = MutableAddress.objects.create( name="Original Billing") source.shipping_address = MutableAddress.objects.create( name="Original Shipping") source.customer = get_person_contact(admin_user) source.payment_method = get_default_payment_method() source.shipping_method = get_default_shipping_method() source.add_line( type=OrderLineType.PRODUCT, product=get_default_product(), supplier=get_default_supplier(), quantity=1, base_unit_price=source.create_price(10), ) source.add_line( type=OrderLineType.OTHER, quantity=1, base_unit_price=source.create_price(10), require_verification=True, ) assert len(source.get_lines()) == 2 source.creator = admin_user creator = OrderCreator() order = creator.create_order(source) return order, source
def test_order_source_parentage(rf, admin_user): source = seed_source(admin_user) product = get_default_product() source.add_line( type=OrderLineType.PRODUCT, product=product, supplier=get_default_supplier(), quantity=1, base_unit_price=source.create_price(10), line_id="parent" ) source.add_line( type=OrderLineType.OTHER, text="Child Line", sku="KIDKIDKID", quantity=1, base_unit_price=source.create_price(5), parent_line_id="parent" ) creator = OrderCreator() order = Order.objects.get(pk=creator.create_order(source).pk) kid_line = order.lines.filter(sku="KIDKIDKID").first() assert kid_line assert kid_line.parent_line.product_id == product.pk
def test_matching_coupon_code(rf): default_price = 10 request, product = _init_test_for_product_without_basket(rf, default_price) discount_amount = 4 coupon_code = CouponCode.objects.create(code="HORSESHOW2018", active=True) coupon_code.shops = [request.shop] discount = Discount.objects.create(active=True, product=product, coupon_code=coupon_code, discount_amount_value=discount_amount) discount.shops.add(request.shop) # No basket means no coupon code in basket which means no discount assert product.get_price_info(request).price == request.shop.create_price( default_price) request, product, basket = _init_test_for_product_with_basket( rf, default_price) assert request.basket == basket # Ok now we have basket, but the coupon is not yet applied assert product.get_price_info(request).price == request.shop.create_price( default_price) # Make sure disabling discount makes coupon un-usable coupon_code_modifier = CouponCodeModule() assert coupon_code_modifier.can_use_code(request.basket, coupon_code.code) discount.active = False discount.save() assert not coupon_code_modifier.can_use_code(request.basket, coupon_code.code) discount.active = True discount.save() assert coupon_code_modifier.can_use_code(request.basket, coupon_code.code) basket.add_code(coupon_code) assert coupon_code.code in basket.codes assert coupon_code.code in request.basket.codes assert product.get_price_info(request).price == request.shop.create_price( default_price - discount_amount) # Apply coupon code after order is created basket.clear_codes() creator = OrderCreator() order = creator.create_order(basket) assert order.taxful_total_price == request.shop.create_price(default_price) # Make sure non active discount can't be used discount.active = False discount.save() order_modifier = CouponCodeModule() assert not order_modifier.use_code(order, coupon_code.code) discount.active = True discount.save() assert isinstance(order_modifier.use_code(order, coupon_code.code), CouponUsage)
def test_order_creator_company_multishop(): with override_settings(SHUUP_MANAGE_CONTACTS_PER_SHOP=True, SHUUP_ENABLE_MULTIPLE_SHOPS=True): company = create_random_company() shop = get_shop(identifier="random-shop", enabled=True) source = seed_source(create_random_user(), shop) source.customer = company source.add_line( type=OrderLineType.PRODUCT, product=get_default_product(), supplier=get_default_supplier(), quantity=1, base_unit_price=source.create_price(10), ) creator = OrderCreator() creator.create_order(source) company.refresh_from_db() assert shop in company.shops.all()
def test_order_creator_can_deal_with_packages(): source = get_order_source_with_a_package() package_product = source.get_lines()[0].product package_def = package_product.get_package_child_to_quantity_map() creator = OrderCreator() order = creator.create_order(source) pids_to_quantities = order.get_product_ids_and_quantities() for child, quantity in six.iteritems(package_def): assert pids_to_quantities[child.pk] == 10 * quantity
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_campaign_with_coupons(rf): status = get_initial_order_status() request, shop, group = initialize_test(rf, False) basket = get_basket(request) supplier = get_default_supplier() for x in range(2): product = create_product(printable_gibberish(), shop, supplier=supplier, default_price="50") basket.add_product(supplier=supplier, shop=shop, product=product, quantity=1) basket.shipping_method = get_shipping_method( shop=shop) # For shippable products dc = Coupon.objects.create(code="TEST", active=True) campaign = BasketCampaign.objects.create(shop=shop, name="test", public_name="test", coupon=dc, active=True) BasketDiscountAmount.objects.create( discount_amount=shop.create_price("20"), campaign=campaign) rule = BasketTotalProductAmountCondition.objects.create(value=2) campaign.conditions.add(rule) campaign.save() assert len(basket.get_final_lines() ) == 3 # no discount was applied because coupon is required basket.add_code(dc.code) assert len(basket.get_final_lines() ) == 4 # now basket has codes so they will be applied too assert OrderLineType.DISCOUNT in [l.type for l in basket.get_final_lines()] # Ensure codes persist between requests, so do what the middleware would, i.e. basket.save() # and then reload the basket: del request.basket basket = get_basket(request) assert basket.codes == [dc.code] assert len(basket.get_final_lines() ) == 3 # now basket has codes so they will be applied too assert OrderLineType.DISCOUNT in [l.type for l in basket.get_final_lines()] basket.status = status creator = OrderCreator(request) order = creator.create_order(basket) assert CouponUsage.objects.filter(order=order).count() == 1 assert CouponUsage.objects.filter(order=order, coupon__code=dc.code).count() == 1
def test_matching_coupon_code(rf): default_price = 10 request, product = _init_test_for_product_without_basket(rf, default_price) discount_amount = 4 coupon_code = CouponCode.objects.create(code="HORSESHOW2018", active=True) coupon_code.shops = [request.shop] discount = Discount.objects.create( active=True, product=product, coupon_code=coupon_code, discount_amount_value=discount_amount) discount.shops.add(request.shop) # No basket means no coupon code in basket which means no discount assert product.get_price_info(request).price == request.shop.create_price(default_price) request, product, basket = _init_test_for_product_with_basket(rf, default_price) assert request.basket == basket # Ok now we have basket, but the coupon is not yet applied assert product.get_price_info(request).price == request.shop.create_price(default_price) # Make sure disabling discount makes coupon un-usable coupon_code_modifier = CouponCodeModule() assert coupon_code_modifier.can_use_code(request.basket, coupon_code.code) discount.active = False discount.save() assert not coupon_code_modifier.can_use_code(request.basket, coupon_code.code) discount.active = True discount.save() assert coupon_code_modifier.can_use_code(request.basket, coupon_code.code) basket.add_code(coupon_code) assert coupon_code.code in basket.codes assert coupon_code.code in request.basket.codes get_price_info(context=request, product=product.id) # Test if get_price_info works with product.id sent assert product.get_price_info(request).price == request.shop.create_price(default_price - discount_amount) # Apply coupon code after order is created basket.clear_codes() creator = OrderCreator() order = creator.create_order(basket) assert order.taxful_total_price == request.shop.create_price(default_price) # Make sure non active discount can't be used discount.active = False discount.save() order_modifier = CouponCodeModule() assert not order_modifier.use_code(order, coupon_code.code) discount.active = True discount.save() assert isinstance(order_modifier.use_code(order, coupon_code.code), CouponUsage)
def test_order_creator_min_total(rf, admin_user): shop = get_default_shop() configuration.set(shop, ORDER_MIN_TOTAL_CONFIG_KEY, Decimal(20)) source = seed_source(admin_user) source.add_line( type=OrderLineType.PRODUCT, product=get_default_product(), supplier=get_default_supplier(), quantity=1, base_unit_price=source.create_price(10), ) creator = OrderCreator() with pytest.raises(ValidationError): creator.create_order(source) configuration.set(shop, ORDER_MIN_TOTAL_CONFIG_KEY, Decimal(1)) creator.create_order(source) # do not mess with other tests configuration.set(shop, ORDER_MIN_TOTAL_CONFIG_KEY, Decimal(0))
def test_order_creator_supplierless_product_line_conversion_should_fail(rf, admin_user): source = seed_source(admin_user) source.add_line( type=OrderLineType.PRODUCT, product=get_default_product(), supplier=None, quantity=1, base_unit_price=source.create_price(10), ) creator = OrderCreator() with pytest.raises(ValueError): order = creator.create_order(source)
def _get_custom_order(regular_user, **kwargs): prices_include_tax = kwargs.pop("prices_include_tax", False) include_basket_campaign = kwargs.pop("include_basket_campaign", False) include_catalog_campaign = kwargs.pop("include_catalog_campaign", False) shop = get_shop(prices_include_tax=prices_include_tax) supplier = get_simple_supplier() if include_basket_campaign: _add_basket_campaign(shop) if include_catalog_campaign: _add_catalog_campaign(shop) _add_taxes() contact = get_person_contact(regular_user) source = BasketishOrderSource(shop) source.status = get_initial_order_status() source.customer = contact ctx = get_pricing_module().get_context_from_data(shop, contact) for product_data in _get_product_data(): quantity = product_data.pop("quantity") product = create_product( sku=product_data.pop("sku"), shop=shop, supplier=supplier, stock_behavior=StockBehavior.STOCKED, tax_class=get_default_tax_class(), **product_data) shop_product = product.get_shop_instance(shop) shop_product.categories.add(get_default_category()) shop_product.save() supplier.adjust_stock(product.id, INITIAL_PRODUCT_QUANTITY) pi = product.get_price_info(ctx) source.add_line( type=OrderLineType.PRODUCT, product=product, supplier=supplier, quantity=quantity, base_unit_price=pi.base_unit_price, discount_amount=pi.discount_amount ) oc = OrderCreator() order = oc.create_order(source) return order
def test_order_customer_groups(rf, admin_user): customer = create_random_person() default_group = get_default_customer_group() default_group.members.add(customer) source = seed_source(admin_user) source.customer = customer source.add_line( type=OrderLineType.PRODUCT, product=get_default_product(), supplier=get_default_supplier(), quantity=1, base_unit_price=source.create_price(10), ) source.add_line( type=OrderLineType.OTHER, quantity=1, base_unit_price=source.create_price(10), require_verification=True, ) creator = OrderCreator() order = creator.create_order(source) assert get_data_dict(source.billing_address) == get_data_dict( order.billing_address) assert get_data_dict(source.shipping_address) == get_data_dict( order.shipping_address) customer = source.customer assert customer == order.customer assert customer.groups.count() == 2 assert order.customer_groups.filter(id=default_group.id).exists() with pytest.raises(ProtectedError): default_group.delete() assert customer.tax_group is not None assert customer.tax_group == order.tax_group with pytest.raises(ProtectedError): customer.tax_group.delete() new_group = create_random_contact_group() new_group.members.add(customer) order.phone = "911" order.save() assert order.customer_groups.filter(id=default_group.id).exists() assert not order.customer_groups.filter(id=new_group.id).exists()
def test_campaign_with_coupons(rf): status = get_initial_order_status() request, shop, group = initialize_test(rf, False) basket = get_basket(request) supplier = get_default_supplier() for x in range(2): product = create_product(printable_gibberish(), shop, supplier=supplier, default_price="50") basket.add_product(supplier=supplier, shop=shop, product=product, quantity=1) basket.shipping_method = get_shipping_method(shop=shop) # For shippable products dc = Coupon.objects.create(code="TEST", active=True) campaign = BasketCampaign.objects.create( shop=shop, name="test", public_name="test", coupon=dc, active=True ) BasketDiscountAmount.objects.create(discount_amount=shop.create_price("20"), campaign=campaign) rule = BasketTotalProductAmountCondition.objects.create(value=2) campaign.conditions.add(rule) campaign.save() assert len(basket.get_final_lines()) == 3 # no discount was applied because coupon is required basket.add_code(dc.code) assert len(basket.get_final_lines()) == 4 # now basket has codes so they will be applied too assert OrderLineType.DISCOUNT in [l.type for l in basket.get_final_lines()] # Ensure codes persist between requests, so do what the middleware would, i.e. basket.save() # and then reload the basket: del request.basket basket = get_basket(request) assert basket.codes == [dc.code] assert len(basket.get_final_lines()) == 3 # now basket has codes so they will be applied too assert OrderLineType.DISCOUNT in [l.type for l in basket.get_final_lines()] basket.status = status creator = OrderCreator(request) order = creator.create_order(basket) assert CouponUsage.objects.filter(order=order).count() == 1 assert CouponUsage.objects.filter(order=order, coupon__code=dc.code).count() == 1
def _get_order(prices_include_tax=False, include_basket_campaign=False, include_catalog_campaign=False): shop = get_shop(prices_include_tax=prices_include_tax) supplier = get_simple_supplier() if "shuup.campaigns" in settings.INSTALLED_APPS: from shuup.campaigns.models.basket_conditions import BasketTotalProductAmountCondition from shuup.campaigns.models.basket_effects import BasketDiscountAmount from shuup.campaigns.models.campaigns import BasketCampaign, CatalogCampaign from shuup.campaigns.models.catalog_filters import CategoryFilter from shuup.campaigns.models.product_effects import ProductDiscountAmount if include_basket_campaign: _add_basket_campaign(shop) if include_catalog_campaign: _add_catalog_campaign(shop) _add_taxes() source = BasketishOrderSource(shop) source.status = get_initial_order_status() ctx = get_pricing_module().get_context_from_data(shop, AnonymousContact()) for product_data in _get_product_data(): quantity = product_data.pop("quantity") product = create_product(sku=product_data.pop("sku"), shop=shop, supplier=supplier, stock_behavior=StockBehavior.STOCKED, tax_class=get_default_tax_class(), **product_data) shop_product = product.get_shop_instance(shop) shop_product.categories.add(get_default_category()) shop_product.save() supplier.adjust_stock(product.id, INITIAL_PRODUCT_QUANTITY) pi = product.get_price_info(ctx) source.add_line(type=OrderLineType.PRODUCT, product=product, supplier=supplier, quantity=quantity, base_unit_price=pi.base_unit_price, discount_amount=pi.discount_amount) oc = OrderCreator() order = oc.create_order(source) return order
def _get_order(prices_include_tax=False, include_basket_campaign=False, include_catalog_campaign=False): shop = get_shop(prices_include_tax=prices_include_tax) supplier = get_simple_supplier() if include_basket_campaign: _add_basket_campaign(shop) if include_catalog_campaign: _add_catalog_campaign(shop) _add_taxes() source = BasketishOrderSource(shop) source.status = get_initial_order_status() ctx = get_pricing_module().get_context_from_data(shop, AnonymousContact()) for product_data in _get_product_data(): quantity = product_data.pop("quantity") product = create_product( sku=product_data.pop("sku"), shop=shop, supplier=supplier, stock_behavior=StockBehavior.STOCKED, tax_class=get_default_tax_class(), **product_data) shop_product = product.get_shop_instance(shop) shop_product.categories.add(get_default_category()) shop_product.save() supplier.adjust_stock(product.id, INITIAL_PRODUCT_QUANTITY) pi = product.get_price_info(ctx) source.add_line( type=OrderLineType.PRODUCT, product=product, supplier=supplier, quantity=quantity, base_unit_price=pi.base_unit_price, discount_amount=pi.discount_amount ) oc = OrderCreator() order = oc.create_order(source) order.create_payment(Money("1", "EUR")) assert not order.has_refunds() assert order.can_create_refund() assert order.shipping_status == ShippingStatus.NOT_SHIPPED assert order.payment_status == PaymentStatus.PARTIALLY_PAID return order
def test_order_customer_groups(rf, admin_user): customer = create_random_person() default_group = get_default_customer_group() default_group.members.add(customer) source = seed_source(admin_user) source.customer=customer source.add_line( type=OrderLineType.PRODUCT, product=get_default_product(), supplier=get_default_supplier(), quantity=1, base_unit_price=source.create_price(10), ) source.add_line( type=OrderLineType.OTHER, quantity=1, base_unit_price=source.create_price(10), require_verification=True, ) creator = OrderCreator() order = creator.create_order(source) assert get_data_dict(source.billing_address) == get_data_dict(order.billing_address) assert get_data_dict(source.shipping_address) == get_data_dict(order.shipping_address) customer = source.customer assert customer == order.customer assert customer.groups.count() == 2 assert order.customer_groups.filter(id=default_group.id).exists() with pytest.raises(ProtectedError): default_group.delete() assert customer.tax_group is not None assert customer.tax_group == order.tax_group with pytest.raises(ProtectedError): customer.tax_group.delete() new_group = create_random_contact_group() new_group.members.add(customer) order.phone = "911" order.save() assert order.customer_groups.filter(id=default_group.id).exists() assert not order.customer_groups.filter(id=new_group.id).exists()
def get_order_and_source(admin_user, product, language, language_fallback): # create original source to tamper with contact = get_person_contact(admin_user) contact.language = language contact.save() assert contact.language == language # contact language is naive source = BasketishOrderSource(get_default_shop()) source.status = get_initial_order_status() source.billing_address = MutableAddress.objects.create( name="Original Billing") source.shipping_address = MutableAddress.objects.create( name="Original Shipping") source.customer = contact source.payment_method = get_default_payment_method() source.shipping_method = get_default_shipping_method() source.add_line( type=OrderLineType.PRODUCT, product=product, supplier=get_default_supplier(), quantity=1, base_unit_price=source.create_price(10), ) source.add_line( type=OrderLineType.OTHER, quantity=1, base_unit_price=source.create_price(10), require_verification=True, ) assert len(source.get_lines()) == 2 source.creator = admin_user assert not source._language # is None because it was not directly assigned assert source.language == language_fallback creator = OrderCreator() order = creator.create_order(source) assert order.language == source.language return order, source
def test_campaign_with_coupons2(rf): basket, dc, request, status = _init_basket_coupon_test(rf, code="tEsT") assert len(basket.get_final_lines() ) == 3 # no discount was applied because coupon is required customer_code = "Test" # Customer typoed the code, should still match basket.add_code(customer_code) assert customer_code in basket.codes assert len(basket.codes) == 1 # only one code basket.add_code(customer_code.upper()) assert customer_code.upper() not in basket.codes assert len(basket.codes) == 1 # only one code assert len(basket.get_final_lines() ) == 4 # now basket has codes so they will be applied too assert OrderLineType.DISCOUNT in [l.type for l in basket.get_final_lines()] # Ensure codes persist between requests, so do what the middleware would, i.e. basket.save() # and then reload the basket: del request.basket basket = get_basket(request) assert basket.codes != [dc.code] # they don't match like this assert [c.upper() for c in basket.codes] == [dc.code.upper()] # they match like this assert [c.upper() for c in basket.codes ] != [customer_code] # they don't match like this assert basket.codes == [customer_code] # they match like this assert len(basket.get_final_lines() ) == 4 # now basket has codes so they will be applied too assert OrderLineType.DISCOUNT in [l.type for l in basket.get_final_lines()] basket.status = status creator = OrderCreator(request) order = creator.create_order(basket) assert CouponUsage.objects.filter(order=order).count() == 1 assert CouponUsage.objects.filter(order=order, coupon__code=dc.code).count() == 1
def get_order_and_source(admin_user, product, language, language_fallback): # create original source to tamper with contact = get_person_contact(admin_user) contact.language = language contact.save() assert contact.language == language # contact language is naive source = BasketishOrderSource(get_default_shop()) source.status = get_initial_order_status() source.billing_address = MutableAddress.objects.create(name="Original Billing") source.shipping_address = MutableAddress.objects.create(name="Original Shipping") source.customer = contact source.payment_method = get_default_payment_method() source.shipping_method = get_default_shipping_method() source.add_line( type=OrderLineType.PRODUCT, product=product, supplier=get_default_supplier(), quantity=1, base_unit_price=source.create_price(10), ) source.add_line( type=OrderLineType.OTHER, quantity=1, base_unit_price=source.create_price(10), require_verification=True, ) assert len(source.get_lines()) == 2 source.creator = admin_user assert not source._language # is None because it was not directly assigned assert source.language == language_fallback creator = OrderCreator() order = creator.create_order(source) assert order.language == source.language return order, source
def test_order_creator(rf, admin_user): source = seed_source(admin_user) source.add_line( type=OrderLineType.PRODUCT, product=get_default_product(), supplier=get_default_supplier(), quantity=1, base_unit_price=source.create_price(10), ) source.add_line(accounting_identifier="strawberries", type=OrderLineType.OTHER, quantity=1, base_unit_price=source.create_price(10), require_verification=True, extra={"runner": "runner"}) the_line = [ sl for sl in source.get_lines() if sl.accounting_identifier == "strawberries" ] assert the_line[0].data["extra"]["runner"] == "runner" creator = OrderCreator() order = creator.create_order(source) assert get_data_dict(source.billing_address) == get_data_dict( order.billing_address) assert get_data_dict(source.shipping_address) == get_data_dict( order.shipping_address) customer = source.customer assert customer == order.customer assert customer.groups.count() == 1 assert customer.groups.first() == order.customer_groups.first() assert customer.tax_group is not None assert customer.tax_group == order.tax_group assert source.payment_method == order.payment_method assert source.shipping_method == order.shipping_method assert order.pk assert order.lines.filter(accounting_identifier="strawberries").first( ).extra_data["runner"] == "runner"
def test_campaign_with_coupons2(rf): basket, dc, request, status = _init_basket_coupon_test(rf, code="tEsT") assert len(basket.get_final_lines()) == 3 # no discount was applied because coupon is required customer_code = "Test" # Customer typoed the code, should still match basket.add_code(customer_code) assert customer_code in basket.codes assert len(basket.codes) == 1 # only one code basket.add_code(customer_code.upper()) assert customer_code.upper() not in basket.codes assert len(basket.codes) == 1 # only one code assert len(basket.get_final_lines()) == 4 # now basket has codes so they will be applied too assert OrderLineType.DISCOUNT in [l.type for l in basket.get_final_lines()] # Ensure codes persist between requests, so do what the middleware would, i.e. basket.save() # and then reload the basket: del request.basket basket = get_basket(request) assert basket.codes != [dc.code] # they don't match like this assert [c.upper() for c in basket.codes] == [dc.code.upper()] # they match like this assert [c.upper() for c in basket.codes] != [customer_code] # they don't match like this assert basket.codes == [customer_code] # they match like this assert len(basket.get_final_lines()) == 3 # now basket has codes so they will be applied too assert OrderLineType.DISCOUNT in [l.type for l in basket.get_final_lines()] basket.status = status creator = OrderCreator(request) order = creator.create_order(basket) assert CouponUsage.objects.filter(order=order).count() == 1 assert CouponUsage.objects.filter(order=order, coupon__code=dc.code).count() == 1
def test_coupons_usage_report(rf): shop = get_default_shop() tax_class = get_default_tax_class() creator = OrderCreator() coupon1 = Coupon.objects.create(code="coupon1", active=True) coupon2 = Coupon.objects.create(code="coupon2", active=True) campaign1 = get_default_campaign(coupon1, "10") campaign2 = get_default_campaign(coupon2, "25") source1 = seed_source(coupon1) source2 = seed_source(coupon1) source3 = seed_source(coupon1) source4 = seed_source(coupon2) creator.create_order(source1) creator.create_order(source2) creator.create_order(source3) creator.create_order(source4) # pay orders [o.create_payment(o.taxful_total_price) for o in Order.objects.all()] data = { "report": CouponsUsageReport.get_name(), "shop": shop.pk, "date_range": DateRangeChoices.ALL_TIME, "writer": "json", "force_download": 1, } report = CouponsUsageReport(**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(CouponsUsageReport.title) in json_data.get("heading") data = json_data.get("tables")[0].get("data") assert len(data) == Order.objects.count() expected_data = [] orders = Order.objects.all().order_by("order_date") for order in orders: discount = order.shop.create_price(0) for dt in order.lines.discounts(): discount += dt.taxful_price expected_data.append({ "date": format_date(order.order_date, locale=get_current_babel_locale()), "coupon": order.codes[0], "order": str(order), "taxful_total": str(order.taxful_total_price.as_rounded().value), "taxful_subtotal": str((order.taxful_total_price - discount).as_rounded().value), "total_discount": str(discount.as_rounded().value) }) assert len(expected_data) == len(data) for ix, d in enumerate(data): for k, v in d.items(): assert expected_data[ix][k] == v
def test_shipping_report(rf): shop = get_default_shop() tax_class = get_default_tax_class() creator = OrderCreator() carrier1 = CustomCarrier.objects.create(name="Carrier1") sm1 = carrier1.create_service(None, shop=get_default_shop(), enabled=True, tax_class=tax_class, name="SM #1") sm1.behavior_components.add(FixedCostBehaviorComponent.objects.create(price_value=Decimal(10))) sm2 = carrier1.create_service(None, shop=get_default_shop(), enabled=True, tax_class=tax_class, name="SM #2") sm2.behavior_components.add(FixedCostBehaviorComponent.objects.create(price_value=Decimal(99))) sm2.behavior_components.add(FixedCostBehaviorComponent.objects.create(price_value=Decimal(4))) carrier2 = CustomCarrier.objects.create(name="Carrier2") sm3 = carrier2.create_service(None, shop=get_default_shop(), enabled=True, tax_class=tax_class, name="SM #3") sm3.behavior_components.add(FixedCostBehaviorComponent.objects.create(price_value=Decimal(5))) source1 = seed_source(sm1) source2 = seed_source(sm1) source3 = seed_source(sm2) source4 = seed_source(sm3) creator.create_order(source1) creator.create_order(source2) creator.create_order(source3) creator.create_order(source4) # pay orders [o.create_payment(o.taxful_total_price) for o in Order.objects.all()] data = { "report": ShippingReport.get_name(), "shop": shop.pk, "date_range": DateRangeChoices.ALL_TIME, "writer": "json", "force_download": 1, } report = ShippingReport(**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(ShippingReport.title) in json_data.get("heading") data = json_data.get("tables")[0].get("data") assert len(data) == 3 expected_result = [ { "carrier": carrier1.name, "shipping_method": sm1.name, "order_count": 2, "total_charged": sum([bc.price_value for bc in sm1.behavior_components.all()]) * 2 # 2 orders }, { "carrier": carrier1.name, "shipping_method": sm2.name, "order_count": 1, "total_charged": sum([bc.price_value for bc in sm2.behavior_components.all()]) }, { "carrier": carrier2.name, "shipping_method": sm3.name, "order_count": 1, "total_charged": sum([bc.price_value for bc in sm3.behavior_components.all()]) } ] for ix, shipping in enumerate(data): assert shipping["carrier"] == expected_result[ix]["carrier"] assert shipping["shipping_method"] == expected_result[ix]["shipping_method"] assert shipping["order_count"] == str(expected_result[ix]["order_count"]) assert shipping["total_charged"] == str(expected_result[ix]["total_charged"].quantize(Decimal("0.01")))
def test_broken_order(admin_user): """ """ quantities = [44, 23, 65] expected = sum(quantities) * 50 expected_based_on = expected / 1.5 # Shuup is calculating taxes per line so there will be some "errors" expected_based_on = ensure_decimal_places(Decimal("%s" % (expected_based_on + 0.01))) shop = get_default_shop() supplier = get_default_supplier() product1 = create_product("simple-test-product1", shop, supplier, 50) product2 = create_product("simple-test-product2", shop, supplier, 50) product3 = create_product("simple-test-product3", shop, supplier, 50) tax = get_default_tax() source = BasketishOrderSource(get_default_shop()) billing_address = get_address(country="US") shipping_address = get_address(name="Test street", country="US") source.status = get_initial_order_status() source.billing_address = billing_address source.shipping_address = shipping_address source.customer = create_random_person() source.payment_method = get_default_payment_method() source.shipping_method = get_default_shipping_method() source.add_line( type=OrderLineType.PRODUCT, product=product1, supplier=get_default_supplier(), quantity=quantities[0], base_unit_price=source.create_price(50), ) source.add_line( type=OrderLineType.PRODUCT, product=product2, supplier=get_default_supplier(), quantity=quantities[1], base_unit_price=source.create_price(50), ) source.add_line( type=OrderLineType.PRODUCT, product=product3, supplier=get_default_supplier(), quantity=quantities[2], base_unit_price=source.create_price(50), ) currency = "EUR" summary = source.get_tax_summary() assert len(summary) == 1 summary = summary[0] assert summary.taxful == Money(expected, "EUR") assert summary.based_on == Money(expected_based_on, "EUR") # originally non-rounded value assert bankers_round(source.get_total_tax_amount()) == summary.tax_amount assert source.taxless_total_price.value == expected_based_on assert summary.taxful.value == source.taxful_total_price.value assert summary.tax_amount == Money(bankers_round(source.taxful_total_price.value - source.taxless_total_price.value), currency) assert summary.taxful == summary.raw_based_on + summary.tax_amount assert summary.tax_rate == tax.rate assert summary.taxful.value == (summary.based_on + summary.tax_amount).value - Decimal("%s" % 0.01) # create order from basket creator = OrderCreator() order = creator.create_order(source) assert order.taxless_total_price.value == expected_based_on # originally non-rounded value assert bankers_round(order.get_total_tax_amount()) == summary.tax_amount