def calculate_order_shipping(self, order, previous_value): price = Money("1.0", currency=order.currency) return TaxedMoney(price, price)
def calculate_order_line_unit(self, order_line, previous_value): currency = order_line.unit_price.currency price = Money("1.0", currency) return TaxedMoney(price, price)
def apply_taxes_to_product(self, product, price, country, previous_value, **kwargs): price = Money("1.0", price.currency) return TaxedMoney(price, price)
def calculate_checkout_subtotal(self, checkout, discounts, previous_value): subtotal = Money("1.0", currency=checkout.currency) return TaxedMoney(subtotal, subtotal)
def zero_taxed_money(currency: str) -> TaxedMoney: zero = zero_money(currency) return TaxedMoney(net=zero, gross=zero)
def test_cart_line_total_with_discount_and_taxes( sale, request_cart_with_item, taxes): sales = Sale.objects.all() line = request_cart_with_item.lines.first() assert line.get_total(discounts=sales, taxes=taxes) == TaxedMoney( net=Money('4.07', 'USD'), gross=Money('5.00', 'USD'))
"is_shipping_required, shipping_method, discount_value, discount_type," "countries, min_spent_amount, min_checkout_items_quantity, subtotal," "total_quantity, error_msg", [ ( True, Mock( get_total=Mock(return_value=Money(10, "USD")), shipping_zone=Mock(countries=["PL"]), ), 10, DiscountValueType.FIXED, ["US"], None, None, TaxedMoney(Money(10, "USD"), Money(10, "USD")), 10, "This offer is not valid in your country.", ), ( True, None, 10, DiscountValueType.FIXED, [], None, None, TaxedMoney(Money(10, "USD"), Money(10, "USD")), 10, "Please select a shipping method first.", ),
def test_create_return_fulfillment_with_lines_already_refunded( mocked_refund, mocked_order_updated, fulfilled_order, payment_dummy_fully_charged, staff_user, channel_USD, variant, warehouse, ): fulfilled_order.payments.add(payment_dummy_fully_charged) payment = fulfilled_order.get_last_payment() order_line_ids = fulfilled_order.lines.all().values_list("id", flat=True) fulfillment_lines = FulfillmentLine.objects.filter(order_line_id__in=order_line_ids) original_quantity = {line.id: line.quantity for line in fulfillment_lines} fulfillment_lines_to_return = fulfillment_lines stock = Stock.objects.create( warehouse=warehouse, product_variant=variant, quantity=5 ) channel_listing = variant.channel_listings.get() net = variant.get_price(variant.product, [], channel_USD, channel_listing) gross = Money(amount=net.amount * Decimal(1.23), currency=net.currency) unit_price = TaxedMoney(net=net, gross=gross) quantity = 5 order_line = fulfilled_order.lines.create( product_name=str(variant.product), variant_name=str(variant), product_sku=variant.sku, product_variant_id=variant.get_global_id(), is_shipping_required=variant.is_shipping_required(), is_gift_card=variant.is_gift_card(), quantity=quantity, quantity_fulfilled=2, variant=variant, unit_price=unit_price, tax_rate=Decimal("0.23"), total_price=unit_price * quantity, ) Allocation.objects.create( order_line=order_line, stock=stock, quantity_allocated=order_line.quantity ) refunded_fulfillment = Fulfillment.objects.create( order=fulfilled_order, status=FulfillmentStatus.REFUNDED ) refunded_fulfillment_line = refunded_fulfillment.lines.create( order_line=order_line, quantity=2 ) fulfillment_lines_to_process = [ FulfillmentLineData(line=line, quantity=2) for line in fulfillment_lines_to_return ] fulfillment_lines_to_process.append( FulfillmentLineData(line=refunded_fulfillment_line, quantity=2) ) create_fulfillments_for_returned_products( user=staff_user, app=None, order=fulfilled_order, payment=payment, order_lines=[], fulfillment_lines=fulfillment_lines_to_process, manager=get_plugins_manager(), refund=True, ) flush_post_commit_hooks() returned_and_refunded_fulfillment = Fulfillment.objects.get( order=fulfilled_order, status=FulfillmentStatus.REFUNDED_AND_RETURNED ) returned_and_refunded_lines = returned_and_refunded_fulfillment.lines.all() assert returned_and_refunded_lines.count() == len(order_line_ids) for fulfillment_line in returned_and_refunded_lines: assert fulfillment_line.quantity == 2 assert fulfillment_line.order_line_id in order_line_ids for line in fulfillment_lines: assert line.quantity == original_quantity.get(line.pk) - 2 # the already refunded line is not included in amount amount = sum( [ line.order_line.unit_price_gross_amount * 2 for line in fulfillment_lines_to_return ] ) mocked_refund.assert_called_once_with( payment_dummy_fully_charged, ANY, amount=amount, channel_slug=fulfilled_order.channel.slug, ) assert returned_and_refunded_fulfillment.total_refund_amount == amount assert returned_and_refunded_fulfillment.shipping_refund_amount is None mocked_order_updated.assert_called_once_with(fulfilled_order)
def test_get_tax_rate_by_name_empty_taxes(product): rate_name = "unexisting tax rate" tax_rate = get_tax_rate_by_name(rate_name) assert tax_rate == 0 @pytest.mark.parametrize( "price, charge_taxes, expected_price", [ ( Money(10, "USD"), False, TaxedMoney(net=Money(10, "USD"), gross=Money(10, "USD")), ), ( Money(10, "USD"), True, TaxedMoney(net=Money("8.13", "USD"), gross=Money(10, "USD")), ), ], ) def test_get_taxed_shipping_price(site_settings, vatlayer, price, charge_taxes, expected_price): site_settings.charge_taxes_on_shipping = charge_taxes site_settings.save() shipping_price = get_taxed_shipping_price(price, taxes=vatlayer)
def test_apply_tax_to_price_no_taxes_return_taxed_money(): money = Money(100, "USD") taxed_money = TaxedMoney(net=Money(100, "USD"), gross=Money(100, "USD")) assert apply_tax_to_price(None, "standard", money) == taxed_money assert apply_tax_to_price(None, "medical", taxed_money) == taxed_money
response = client.post( reverse("checkout:update-line", kwargs={"variant_id": variant.id}), {"quantity": 3}, HTTP_X_REQUESTED_WITH="XMLHttpRequest", ) assert response.status_code == 200 assert request_checkout_with_item.quantity == 3 @pytest.mark.parametrize( "price, charge_taxes, expected_price", [ ( Money(10, "MXN"), False, TaxedMoney(net=Money(10, "MXN"), gross=Money(10, "MXN")), ), ( Money(10, "MXN"), True, TaxedMoney(net=Money("8.13", "MXN"), gross=Money(10, "MXN")), ), ], ) def test_get_taxed_shipping_price( site_settings, vatlayer, price, charge_taxes, expected_price ): site_settings.charge_taxes_on_shipping = charge_taxes site_settings.save() shipping_price = get_taxed_shipping_price(price, taxes=vatlayer)
def test_checkout_total_with_discount(request_checkout_with_item, discount_info, vatlayer): total = request_checkout_with_item.get_total(discounts=[discount_info], taxes=vatlayer) assert total == TaxedMoney(net=Money("4.07", "USD"), gross=Money("5.00", "USD"))
from django.conf import settings from django.contrib.sites.models import Site from django_countries.fields import Country from django_prices_vatlayer.utils import (get_tax_for_rate, get_tax_rates_for_country) from prices import Money, MoneyRange, TaxedMoney, TaxedMoneyRange DEFAULT_TAX_RATE_NAME = 'standard' ZERO_MONEY = Money(0, settings.DEFAULT_CURRENCY) ZERO_TAXED_MONEY = TaxedMoney(net=ZERO_MONEY, gross=ZERO_MONEY) def apply_tax_to_price(taxes, rate_name, base): if not taxes or not rate_name: # Naively convert Money to TaxedMoney for consistency with price # handling logic across the codebase, passthrough other money types if isinstance(base, Money): return TaxedMoney(net=base, gross=base) if isinstance(base, MoneyRange): return TaxedMoneyRange( apply_tax_to_price(taxes, rate_name, base.start), apply_tax_to_price(taxes, rate_name, base.stop)) if isinstance(base, (TaxedMoney, TaxedMoneyRange)): return base raise TypeError('Unknown base for flat_tax: %r' % (base, )) if rate_name in taxes: tax_to_apply = taxes[rate_name]['tax'] else: tax_to_apply = taxes[DEFAULT_TAX_RATE_NAME]['tax']
def create_order_lines(order, discounts, how_many=10): channel = order.channel available_variant_ids = channel.variant_listings.values_list( "variant_id", flat=True ) variants = ( ProductVariant.objects.filter(pk__in=available_variant_ids) .order_by("?") .prefetch_related("product__product_type")[:how_many] ) variants_iter = itertools.cycle(variants) lines = [] for _ in range(how_many): variant = next(variants_iter) variant_channel_listing = variant.channel_listings.get(channel=channel) product = variant.product quantity = random.randrange(1, 5) unit_price = variant.get_price( product, product.collections.all(), channel, variant_channel_listing, discounts, ) unit_price = TaxedMoney(net=unit_price, gross=unit_price) total_price = unit_price * quantity lines.append( OrderLine( order=order, product_name=str(product), variant_name=str(variant), product_sku=variant.sku, is_shipping_required=variant.is_shipping_required(), quantity=quantity, variant=variant, unit_price=unit_price, total_price=total_price, tax_rate=0, ) ) lines = OrderLine.objects.bulk_create(lines) manager = get_plugins_manager() country = order.shipping_method.shipping_zone.countries[0] warehouses = Warehouse.objects.filter( shipping_zones__countries__contains=country ).order_by("?") warehouse_iter = itertools.cycle(warehouses) for line in lines: variant = line.variant unit_price = manager.calculate_order_line_unit( order, line, variant, variant.product ) line.unit_price = unit_price line.tax_rate = unit_price.tax / unit_price.net warehouse = next(warehouse_iter) increase_stock(line, warehouse, line.quantity, allocate=True) OrderLine.objects.bulk_update( lines, ["unit_price_net_amount", "unit_price_gross_amount", "currency", "tax_rate"], ) return lines
def add_variant_to_order(order, variant, quantity, user, app, manager, discounts=None, allocate_stock=False): """Add total_quantity of variant to order. Returns an order line the variant was added to. """ channel = order.channel try: line = order.lines.get(variant=variant) old_quantity = line.quantity new_quantity = old_quantity + quantity line_info = OrderLineData(line=line, quantity=old_quantity) change_order_line_quantity( user, app, line_info, old_quantity, new_quantity, channel.slug, manager=manager, send_event=False, ) except OrderLine.DoesNotExist: product = variant.product collections = product.collections.all() channel_listing = variant.channel_listings.get(channel=channel) unit_price = variant.get_price(product, collections, channel, channel_listing, discounts) unit_price = TaxedMoney(net=unit_price, gross=unit_price) total_price = unit_price * quantity product_name = str(product) variant_name = str(variant) translated_product_name = str(product.translated) translated_variant_name = str(variant.translated) if translated_product_name == product_name: translated_product_name = "" if translated_variant_name == variant_name: translated_variant_name = "" line = order.lines.create( product_name=product_name, variant_name=variant_name, translated_product_name=translated_product_name, translated_variant_name=translated_variant_name, product_sku=variant.sku, is_shipping_required=variant.is_shipping_required(), quantity=quantity, unit_price=unit_price, total_price=total_price, variant=variant, ) unit_price = manager.calculate_order_line_unit(order, line, variant, product) total_price = manager.calculate_order_line_total( order, line, variant, product) line.unit_price = unit_price line.total_price = total_price line.undiscounted_unit_price = unit_price line.undiscounted_total_price = total_price line.tax_rate = manager.get_order_line_tax_rate( order, product, variant, None, unit_price) line.save(update_fields=[ "currency", "unit_price_net_amount", "unit_price_gross_amount", "total_price_net_amount", "total_price_gross_amount", "undiscounted_unit_price_gross_amount", "undiscounted_unit_price_net_amount", "undiscounted_total_price_gross_amount", "undiscounted_total_price_net_amount", "tax_rate", ]) if allocate_stock: increase_allocations( [ OrderLineData( line=line, quantity=quantity, variant=variant, warehouse_pk=None, ) ], channel.slug, manager=manager, ) return line
tax_rate = PluginsManager(plugins=plugins).get_checkout_line_tax_rate( checkout_info, lines, checkout_line_info, checkout_with_item.shipping_address, [discount_info], unit_price, ) assert tax_rate == Decimal("0.08") @pytest.mark.parametrize( "unit_price, expected_tax_rate", [ (TaxedMoney(Money(12, "USD"), Money(15, "USD")), Decimal("0.25")), (Decimal("0.0"), Decimal("0.0")), ], ) def test_manager_get_checkout_line_tax_rate_no_plugins(checkout_with_item, discount_info, unit_price, expected_tax_rate): manager = get_plugins_manager() lines = fetch_checkout_lines(checkout_with_item) checkout_info = fetch_checkout_info(checkout_with_item, lines, [discount_info], manager) checkout_line_info = lines[0] tax_rate = PluginsManager(plugins=[]).get_checkout_line_tax_rate( checkout_info, lines,
def sum_order_totals(qs, currency_code): zero = Money(0, currency=currency_code) taxed_zero = TaxedMoney(zero, zero) return sum([order.total for order in qs], taxed_zero)
def sum_order_totals(qs): zero = Money(0, currency=settings.DEFAULT_CURRENCY) taxed_zero = TaxedMoney(zero, zero) return sum([order.total for order in qs], taxed_zero)
def test_delete_products( staff_api_client, product_list, permission_manage_products, order_list, channel_USD ): # given query = DELETE_PRODUCTS_MUTATION not_draft_order = order_list[0] draft_order = order_list[1] draft_order.status = OrderStatus.DRAFT draft_order.save(update_fields=["status"]) draft_order_lines_pks = [] not_draft_order_lines_pks = [] for variant in [product_list[0].variants.first(), product_list[1].variants.first()]: net = variant.get_price(channel_USD.slug) gross = Money(amount=net.amount, currency=net.currency) order_line = OrderLine.objects.create( variant=variant, order=draft_order, product_name=str(variant.product), variant_name=str(variant), product_sku=variant.sku, is_shipping_required=variant.is_shipping_required(), unit_price=TaxedMoney(net=net, gross=gross), quantity=3, ) draft_order_lines_pks.append(order_line.pk) order_line_not_draft = OrderLine.objects.create( variant=variant, order=not_draft_order, product_name=str(variant.product), variant_name=str(variant), product_sku=variant.sku, is_shipping_required=variant.is_shipping_required(), unit_price=TaxedMoney(net=net, gross=gross), quantity=3, ) not_draft_order_lines_pks.append(order_line_not_draft.pk) variables = { "ids": [ graphene.Node.to_global_id("Product", product.id) for product in product_list ] } # when response = staff_api_client.post_graphql( query, variables, permissions=[permission_manage_products] ) # then content = get_graphql_content(response) assert content["data"]["productBulkDelete"]["count"] == 3 assert not Product.objects.filter( id__in=[product.id for product in product_list] ).exists() assert not OrderLine.objects.filter(pk__in=draft_order_lines_pks).exists() assert OrderLine.objects.filter(pk__in=not_draft_order_lines_pks).exists()
def calculate_checkout_total(self, checkout, lines, discounts, previous_value): total = Money("1.0", currency=checkout.currency) return TaxedMoney(total, total)
def test_delete_product_variants_in_draft_orders( staff_api_client, product_variant_list, permission_manage_products, order_line, order_list, channel_USD, ): # given query = PRODUCT_VARIANT_BULK_DELETE_MUTATION variants = product_variant_list draft_order = order_line.order draft_order.status = OrderStatus.DRAFT draft_order.save(update_fields=["status"]) second_variant_in_draft = variants[1] net = second_variant_in_draft.get_price(channel_USD.slug) gross = Money(amount=net.amount, currency=net.currency) second_draft_order = OrderLine.objects.create( variant=second_variant_in_draft, order=draft_order, product_name=str(second_variant_in_draft.product), variant_name=str(second_variant_in_draft), product_sku=second_variant_in_draft.sku, is_shipping_required=second_variant_in_draft.is_shipping_required(), unit_price=TaxedMoney(net=net, gross=gross), quantity=3, ) variant = variants[0] net = variant.get_price(channel_USD.slug) gross = Money(amount=net.amount, currency=net.currency) order_not_draft = order_list[-1] order_line_not_in_draft = OrderLine.objects.create( variant=variant, order=order_not_draft, product_name=str(variant.product), variant_name=str(variant), product_sku=variant.sku, is_shipping_required=variant.is_shipping_required(), unit_price=TaxedMoney(net=net, gross=gross), quantity=3, ) order_line_not_in_draft_pk = order_line_not_in_draft.pk variant_count = ProductVariant.objects.count() variables = { "ids": [ graphene.Node.to_global_id("ProductVariant", variant.id) for variant in ProductVariant.objects.all() ] } # when response = staff_api_client.post_graphql( query, variables, permissions=[permission_manage_products] ) # then content = get_graphql_content(response) assert content["data"]["productVariantBulkDelete"]["count"] == variant_count assert not ProductVariant.objects.filter( id__in=[variant.id for variant in product_variant_list] ).exists() with pytest.raises(order_line._meta.model.DoesNotExist): order_line.refresh_from_db() with pytest.raises(second_draft_order._meta.model.DoesNotExist): second_draft_order.refresh_from_db() assert OrderLine.objects.filter(pk=order_line_not_in_draft_pk).exists()
def test_cart_total_with_discount(request_cart_with_item, sale, vatlayer): total = ( request_cart_with_item.get_total(discounts=(sale,), taxes=vatlayer)) assert total == TaxedMoney( net=Money('4.07', 'USD'), gross=Money('5.00', 'USD'))
def test_fulfillment_refund_products_fulfillment_lines_and_order_lines( mocked_refund, warehouse, variant, channel_USD, staff_api_client, permission_manage_orders, fulfilled_order, payment_dummy, ): payment_dummy.total = fulfilled_order.total_gross_amount payment_dummy.captured_amount = payment_dummy.total payment_dummy.charge_status = ChargeStatus.FULLY_CHARGED payment_dummy.save() fulfilled_order.payments.add(payment_dummy) stock = Stock.objects.create(warehouse=warehouse, product_variant=variant, quantity=5) channel_listing = variant.channel_listings.get() net = variant.get_price(variant.product, [], channel_USD, channel_listing) gross = Money(amount=net.amount * Decimal(1.23), currency=net.currency) variant.track_inventory = False variant.save() unit_price = TaxedMoney(net=net, gross=gross) quantity = 5 order_line = fulfilled_order.lines.create( product_name=str(variant.product), variant_name=str(variant), product_sku=variant.sku, is_shipping_required=variant.is_shipping_required(), quantity=quantity, quantity_fulfilled=2, variant=variant, unit_price=unit_price, total_price=unit_price * quantity, tax_rate=Decimal("0.23"), ) fulfillment = fulfilled_order.fulfillments.get() fulfillment.lines.create(order_line=order_line, quantity=2, stock=stock) fulfillment_line_to_refund = fulfilled_order.fulfillments.first( ).lines.first() order_id = graphene.Node.to_global_id("Order", fulfilled_order.pk) fulfillment_line_id = graphene.Node.to_global_id( "FulfillmentLine", fulfillment_line_to_refund.pk) order_line_from_fulfillment_line = graphene.Node.to_global_id( "OrderLine", fulfillment_line_to_refund.order_line.pk) order_line_id = graphene.Node.to_global_id("OrderLine", order_line.pk) variables = { "order": order_id, "input": { "orderLines": [{ "orderLineId": order_line_id, "quantity": 2 }], "fulfillmentLines": [{ "fulfillmentLineId": fulfillment_line_id, "quantity": 2 }], }, } staff_api_client.user.user_permissions.add(permission_manage_orders) response = staff_api_client.post_graphql(ORDER_FULFILL_REFUND_MUTATION, variables) content = get_graphql_content(response) data = content["data"]["orderFulfillmentRefundProducts"] refund_fulfillment = data["fulfillment"] errors = data["errors"] assert not errors assert refund_fulfillment["status"] == FulfillmentStatus.REFUNDED.upper() assert len(refund_fulfillment["lines"]) == 2 assert refund_fulfillment["lines"][0]["orderLine"]["id"] in [ order_line_id, order_line_from_fulfillment_line, ] assert refund_fulfillment["lines"][0]["quantity"] == 2 assert refund_fulfillment["lines"][1]["orderLine"]["id"] in [ order_line_id, order_line_from_fulfillment_line, ] assert refund_fulfillment["lines"][1]["quantity"] == 2 amount = fulfillment_line_to_refund.order_line.unit_price_gross_amount * 2 amount += order_line.unit_price_gross_amount * 2 amount = quantize_price(amount, fulfilled_order.currency) mocked_refund.assert_called_with(payment_dummy, ANY, amount=amount, channel_slug=fulfilled_order.channel.slug)
def apply_taxes_to_shipping(self, price, shipping_address, previous_value) -> TaxedMoney: price = Money("1.0", price.currency) return TaxedMoney(price, price)
def order_with_lines(order, product_type, category, shipping_zone): product = Product.objects.create( name="Test product", price=Money("10.00", "USD"), product_type=product_type, category=category, ) variant = ProductVariant.objects.create( product=product, sku="SKU_A", cost_price=Money(1, "USD"), quantity=5, quantity_allocated=3, ) net = variant.get_price() gross = Money(amount=net.amount * Decimal(1.23), currency=net.currency) order.lines.create( product_name=str(variant.product), variant_name=str(variant), product_sku=variant.sku, is_shipping_required=variant.is_shipping_required(), quantity=3, variant=variant, unit_price=TaxedMoney(net=net, gross=gross), tax_rate=23, ) product = Product.objects.create( name="Test product 2", price=Money("20.00", "USD"), product_type=product_type, category=category, ) variant = ProductVariant.objects.create( product=product, sku="SKU_B", cost_price=Money(2, "USD"), quantity=2, quantity_allocated=2, ) net = variant.get_price() gross = Money(amount=net.amount * Decimal(1.23), currency=net.currency) order.lines.create( product_name=str(variant.product), variant_name=str(variant), product_sku=variant.sku, is_shipping_required=variant.is_shipping_required(), quantity=2, variant=variant, unit_price=TaxedMoney(net=net, gross=gross), tax_rate=23, ) order.shipping_address = order.billing_address.get_copy() method = shipping_zone.shipping_methods.get() order.shipping_method_name = method.name order.shipping_method = method net = method.get_total() gross = Money(amount=net.amount * Decimal(1.23), currency=net.currency) order.shipping_price = TaxedMoney(net=net, gross=gross) order.save() recalculate_order(order) order.refresh_from_db() return order
def calculate_checkout_shipping(self, checkout, discounts, previous_value): price = Money("1.0", currency=checkout.currency) return TaxedMoney(price, price)
def test_manager_apply_taxes_to_shipping(shipping_method, address, plugins, price_amount): expected_price = Money(price_amount, "USD") taxed_price = ExtensionsManager(plugins=plugins).apply_taxes_to_shipping( shipping_method.price, address) assert TaxedMoney(expected_price, expected_price) == taxed_price
def calculate_checkout_line_total(self, checkout_line, discounts, previous_value): price = Money("1.0", currency=checkout_line.checkout.currency) return TaxedMoney(price, price)
def get_total_price(self): return TaxedMoney(net=self.price, gross=self.price)
def test_quantize(): price = TaxedMoney(Money('1.001', 'EUR'), Money('1.001', 'EUR')) assert price.quantize() == ( TaxedMoney(Money('1.00', 'EUR'), Money('1.00', 'EUR')))
def get_total(self): if self.cost_price: return TaxedMoney(net=self.cost_price, gross=self.cost_price)