diff --git a/src/pretix/api/serializers/__init__.py b/src/pretix/api/serializers/__init__.py index ea435914ac..16e8bb7e96 100644 --- a/src/pretix/api/serializers/__init__.py +++ b/src/pretix/api/serializers/__init__.py @@ -20,6 +20,7 @@ # . # import json +import re from django.db.models import prefetch_related_objects from rest_framework import serializers @@ -135,3 +136,30 @@ class SalesChannelMigrationMixin: else: value["sales_channels"] = value["limit_sales_channels"] return value + + +class CompatDecimalField(serializers.DecimalField): + """ + Historically, pretix recorded tax rates as decimals with two places. Today, pretix supports tax rates with up to + four places. Since our API outputs decimals with the stored precision, this would have changed the API output from + "19.00" to "19.0000" without warning. While this is semantically the same thing, we need to assume some pretix API + users might run into trouble, either because they treat the value as a string and then map something + (e.g. ``if tax_rate == "19.00"``) or process it with a language where this is a significant difference. For example, + while in Python ``Decimal("19.00") == Decimal("19.0000")`` is true, in Java + ``(new BigDecimal("19.00")).equals(new BigDecimal("19.0000"))`` is false and only + ``(new BigDecimal("19.00")).compareTo(new BigDecimal("19.0000")) == 0`` is true. + + Therefore, we stay backwards compatible by outputting two decimal places *as long as the trailing digits are zero-valued. + """ + + regex = re.compile(r"^([0-9]+\.[0-9]{2})0+$") + + def to_representation(self, value): + if self.localize: + raise ValueError("localization not supported") + value = super().to_representation(value) + if value and "." not in value: + return f"{value}.00" + if m := self.regex.match(value): + return m.group(1) + return value diff --git a/src/pretix/api/serializers/event.py b/src/pretix/api/serializers/event.py index ca163f0d33..94200ff804 100644 --- a/src/pretix/api/serializers/event.py +++ b/src/pretix/api/serializers/event.py @@ -48,7 +48,7 @@ from rest_framework.fields import ChoiceField, Field from rest_framework.relations import SlugRelatedField from pretix.api.serializers import ( - CompatibleJSONField, SalesChannelMigrationMixin, + CompatDecimalField, CompatibleJSONField, SalesChannelMigrationMixin, ) from pretix.api.serializers.fields import PluginsField from pretix.api.serializers.i18n import I18nAwareModelSerializer @@ -681,6 +681,7 @@ class TaxRuleSerializer(CountryFieldMixin, I18nAwareModelSerializer): required=False, allow_null=True, ) + rate = CompatDecimalField(max_digits=7, decimal_places=4) class Meta: model = TaxRule diff --git a/src/pretix/api/serializers/item.py b/src/pretix/api/serializers/item.py index a2c6258f5e..95a9825476 100644 --- a/src/pretix/api/serializers/item.py +++ b/src/pretix/api/serializers/item.py @@ -42,7 +42,9 @@ from django.utils.functional import cached_property, lazy from django.utils.translation import gettext_lazy as _ from rest_framework import serializers -from pretix.api.serializers import SalesChannelMigrationMixin +from pretix.api.serializers import ( + CompatDecimalField, SalesChannelMigrationMixin, +) from pretix.api.serializers.event import MetaDataField from pretix.api.serializers.fields import UploadedFileField from pretix.api.serializers.i18n import I18nAwareModelSerializer @@ -276,10 +278,10 @@ class ItemAddOnSerializer(serializers.ModelSerializer): return value -class ItemTaxRateField(serializers.Field): +class ItemTaxRateField(CompatDecimalField): def to_representation(self, i): if i.tax_rule: - return str(Decimal(i.tax_rule.rate)) + return super().to_representation(Decimal(i.tax_rule.rate)) else: return str(Decimal('0.00')) @@ -289,7 +291,7 @@ class ItemSerializer(SalesChannelMigrationMixin, I18nAwareModelSerializer): bundles = InlineItemBundleSerializer(many=True, required=False) variations = InlineItemVariationSerializer(many=True, required=False) program_times = InlineItemProgramTimeSerializer(many=True, required=False) - tax_rate = ItemTaxRateField(source='*', read_only=True) + tax_rate = ItemTaxRateField(source='*', read_only=True, max_digits=7, decimal_places=4) meta_data = MetaDataField(required=False, source='*') picture = UploadedFileField(required=False, allow_null=True, allowed_types=( 'image/png', 'image/jpeg', 'image/gif' diff --git a/src/pretix/api/serializers/order.py b/src/pretix/api/serializers/order.py index f402e7fc99..a03dab4e50 100644 --- a/src/pretix/api/serializers/order.py +++ b/src/pretix/api/serializers/order.py @@ -41,7 +41,7 @@ from rest_framework.exceptions import ValidationError from rest_framework.relations import SlugRelatedField from rest_framework.reverse import reverse -from pretix.api.serializers import CompatibleJSONField +from pretix.api.serializers import CompatDecimalField, CompatibleJSONField from pretix.api.serializers.event import SubEventSerializer from pretix.api.serializers.forms import form_field_to_serializer_field from pretix.api.serializers.i18n import I18nAwareModelSerializer @@ -591,6 +591,7 @@ class OrderPositionSerializer(I18nAwareModelSerializer): country = CompatibleCountryField(source='*') attendee_name = serializers.CharField(required=False) plugin_data = OrderPositionPluginDataField(source='*', allow_null=True, read_only=True) + tax_rate = CompatDecimalField(max_digits=7, decimal_places=4) class Meta: list_serializer_class = OrderPositionListSerializer @@ -747,6 +748,8 @@ class OrderPaymentDateField(serializers.DateField): class OrderFeeSerializer(I18nAwareModelSerializer): + tax_rate = CompatDecimalField(max_digits=7, decimal_places=4) + class Meta: model = OrderFee fields = ('id', 'fee_type', 'value', 'description', 'internal_type', 'tax_rate', 'tax_value', 'tax_rule', @@ -1897,6 +1900,7 @@ class InlineInvoiceLineSerializer(I18nAwareModelSerializer): position = LinePositionField(read_only=True) event_date_from = serializers.DateTimeField(read_only=True, source="period_start") event_date_to = serializers.DateTimeField(read_only=True, source="period_end") + tax_rate = CompatDecimalField(max_digits=7, decimal_places=4) class Meta: model = InvoiceLine @@ -1980,6 +1984,7 @@ class BlockedTicketSecretSerializer(I18nAwareModelSerializer): class TransactionSerializer(I18nAwareModelSerializer): order = serializers.SlugRelatedField(slug_field="code", read_only=True) + tax_rate = CompatDecimalField(max_digits=7, decimal_places=4) class Meta: model = Transaction diff --git a/src/pretix/base/migrations/0299_tax_rate_decimals.py b/src/pretix/base/migrations/0299_tax_rate_decimals.py new file mode 100644 index 0000000000..fbbfcfae94 --- /dev/null +++ b/src/pretix/base/migrations/0299_tax_rate_decimals.py @@ -0,0 +1,48 @@ +# Generated by Django 5.2.12 on 2026-04-15 20:10 + +from decimal import Decimal + +from django.db import migrations, models + +import pretix.helpers.models + + +class Migration(migrations.Migration): + + dependencies = [ + ('pretixbase', '0298_pluggable_permissions'), + ] + + operations = [ + migrations.AlterField( + model_name='cartposition', + name='tax_rate', + field=pretix.helpers.models.NormalizedDecimalField(decimal_places=4, default=Decimal('0'), max_digits=7), + ), + migrations.AlterField( + model_name='invoiceline', + name='tax_rate', + field=pretix.helpers.models.NormalizedDecimalField(decimal_places=4, default=Decimal('0'), max_digits=7), + ), + migrations.AlterField( + model_name='orderfee', + name='tax_rate', + field=pretix.helpers.models.NormalizedDecimalField(decimal_places=4, max_digits=7), + ), + migrations.AlterField( + model_name='orderposition', + name='tax_rate', + field=pretix.helpers.models.NormalizedDecimalField(decimal_places=4, max_digits=7), + ), + migrations.AlterField( + model_name='transaction', + name='tax_rate', + field=pretix.helpers.models.NormalizedDecimalField(decimal_places=4, max_digits=7), + ), + migrations.AlterField( + model_name='taxrule', + name='rate', + field=pretix.helpers.models.NormalizedDecimalField(decimal_places=4, max_digits=7), + ), + + ] diff --git a/src/pretix/base/models/invoices.py b/src/pretix/base/models/invoices.py index e4e9eaa6c2..d0787b7321 100644 --- a/src/pretix/base/models/invoices.py +++ b/src/pretix/base/models/invoices.py @@ -49,6 +49,7 @@ from django_scopes import ScopedManager from pretix.base.settings import COUNTRIES_WITH_STATE_IN_ADDRESS from pretix.helpers.countries import FastCountryField +from pretix.helpers.models import NormalizedDecimalField def invoice_filename(instance, filename: str) -> str: @@ -450,7 +451,7 @@ class InvoiceLine(models.Model): description = models.TextField() gross_value = models.DecimalField(max_digits=13, decimal_places=2) tax_value = models.DecimalField(max_digits=13, decimal_places=2, default=Decimal('0.00')) - tax_rate = models.DecimalField(max_digits=7, decimal_places=2, default=Decimal('0.00')) + tax_rate = NormalizedDecimalField(max_digits=7, decimal_places=4, default=Decimal('0')) tax_name = models.CharField(max_length=190) tax_code = models.CharField(max_length=190, null=True, blank=True) subevent = models.ForeignKey('SubEvent', null=True, blank=True, on_delete=models.PROTECT) diff --git a/src/pretix/base/models/orders.py b/src/pretix/base/models/orders.py index e74e450e8c..9e2bfdd773 100644 --- a/src/pretix/base/models/orders.py +++ b/src/pretix/base/models/orders.py @@ -87,6 +87,7 @@ from pretix.base.timemachine import time_machine_now from ...helpers import OF_SELF from ...helpers.countries import CachedCountries, FastCountryField +from ...helpers.models import NormalizedDecimalField from ...helpers.names import build_name from ...testutils.middleware import debugflags_var from ._transactions import ( @@ -2334,8 +2335,8 @@ class OrderFee(RoundingCorrectionMixin, models.Model): ) description = models.CharField(max_length=190, blank=True) internal_type = models.CharField(max_length=255, blank=True) - tax_rate = models.DecimalField( - max_digits=7, decimal_places=2, + tax_rate = NormalizedDecimalField( + max_digits=7, decimal_places=4, verbose_name=_('Tax rate') ) tax_rule = models.ForeignKey( @@ -2533,8 +2534,8 @@ class OrderPosition(AbstractPosition): max_digits=13, decimal_places=2, null=True, blank=True, ) - tax_rate = models.DecimalField( - max_digits=7, decimal_places=2, + tax_rate = NormalizedDecimalField( + max_digits=7, decimal_places=4, verbose_name=_('Tax rate') ) tax_rule = models.ForeignKey( @@ -3052,8 +3053,8 @@ class Transaction(models.Model): price_includes_rounding_correction = models.DecimalField( max_digits=13, decimal_places=2, default=Decimal("0.00") ) - tax_rate = models.DecimalField( - max_digits=7, decimal_places=2, + tax_rate = NormalizedDecimalField( + max_digits=7, decimal_places=4, verbose_name=_('Tax rate') ) tax_rule = models.ForeignKey( @@ -3168,8 +3169,8 @@ class CartPosition(AbstractPosition): verbose_name=_("Limit for extending expiration date"), null=True ) - tax_rate = models.DecimalField( - max_digits=7, decimal_places=2, default=Decimal('0.00'), + tax_rate = NormalizedDecimalField( + max_digits=7, decimal_places=4, default=Decimal('0'), verbose_name=_('Tax rate') ) tax_code = models.CharField( diff --git a/src/pretix/base/models/tax.py b/src/pretix/base/models/tax.py index 70872d5bbc..4dc8120515 100644 --- a/src/pretix/base/models/tax.py +++ b/src/pretix/base/models/tax.py @@ -40,6 +40,7 @@ from pretix.base.decimal import round_decimal from pretix.base.models.base import LoggedModel from pretix.base.templatetags.money import money_filter from pretix.helpers.countries import FastCountryField +from pretix.helpers.models import NormalizedDecimalField class TaxedPrice: @@ -335,9 +336,9 @@ class TaxRule(LoggedModel): max_length=190, choices=TAX_CODE_LISTS, ) - rate = models.DecimalField( - max_digits=10, - decimal_places=2, + rate = NormalizedDecimalField( + max_digits=7, + decimal_places=4, validators=[ MaxValueValidator( limit_value=Decimal("100.00"), diff --git a/src/pretix/base/templatetags/money.py b/src/pretix/base/templatetags/money.py index e45468411d..bb3f674e73 100644 --- a/src/pretix/base/templatetags/money.py +++ b/src/pretix/base/templatetags/money.py @@ -26,6 +26,8 @@ from babel.numbers import format_currency from django import template from django.conf import settings from django.template.defaultfilters import floatformat +from django.utils import formats +from django.utils.safestring import mark_safe from pretix.base.i18n import get_babel_locale @@ -82,3 +84,19 @@ def money_numberfield_filter(value: Decimal, arg=''): places = settings.CURRENCY_PLACES.get(arg, 2) return str(value.quantize(Decimal('1') / 10 ** places, ROUND_HALF_UP)) + + +@register.filter(is_safe=True) +def tax_rate_format(number): + """ + Display a Decimal to its significant decimal places, used for tax rates. + """ + assert isinstance(number, Decimal) + return mark_safe( + formats.number_format( + number.normalize(), + -number.as_tuple().exponent, + use_l10n=True, + force_grouping=False, + ) + ) diff --git a/src/pretix/control/templates/pretixcontrol/items/index.html b/src/pretix/control/templates/pretixcontrol/items/index.html index 08d39ffa5f..16a2e8832a 100644 --- a/src/pretix/control/templates/pretixcontrol/items/index.html +++ b/src/pretix/control/templates/pretixcontrol/items/index.html @@ -156,11 +156,11 @@
{% if not i.tax_rule.price_includes_tax %} - {% blocktrans trimmed with rate=i.tax_rule.rate|floatformat:-2 taxname=i.tax_rule.name %} + {% blocktrans trimmed with rate=i.tax_rule.rate|tax_rate_format taxname=i.tax_rule.name %} plus {{ rate }}% {{ taxname }} {% endblocktrans %} {% else %} - {% blocktrans trimmed with rate=i.tax_rule.rate|floatformat:-2 taxname=i.tax_rule.name|default:s_taxes %} + {% blocktrans trimmed with rate=i.tax_rule.rate|tax_rate_format taxname=i.tax_rule.name|default:s_taxes %} incl. {{ rate }}% {{ taxname }} {% endblocktrans %} {% endif %} diff --git a/src/pretix/control/templates/pretixcontrol/order/index.html b/src/pretix/control/templates/pretixcontrol/order/index.html index 1d3fa472df..3541093f53 100644 --- a/src/pretix/control/templates/pretixcontrol/order/index.html +++ b/src/pretix/control/templates/pretixcontrol/order/index.html @@ -670,7 +670,7 @@ {% if line.tax_rate %}
- {% blocktrans trimmed with rate=line.tax_rate|floatformat:-2 taxname=line.tax_rule.name|default:s_taxes %} + {% blocktrans trimmed with rate=line.tax_rate|tax_rate_format taxname=line.tax_rule.name|default:s_taxes %} plus {{ rate }}% {{ taxname }} {% endblocktrans %} @@ -680,7 +680,7 @@ {% if line.tax_rate and line.price %}
- {% blocktrans trimmed with rate=line.tax_rate|floatformat:-2 taxname=line.tax_rule.name|default:s_taxes %} + {% blocktrans trimmed with rate=line.tax_rate|tax_rate_format taxname=line.tax_rule.name|default:s_taxes %} incl. {{ rate }}% {{ taxname }} {% endblocktrans %} @@ -720,7 +720,7 @@ {% if fee.tax_rate %}
- {% blocktrans trimmed with rate=fee.tax_rate|floatformat:-2 taxname=fee.tax_rule.name|default:s_taxes %} + {% blocktrans trimmed with rate=fee.tax_rate|tax_rate_format taxname=fee.tax_rule.name|default:s_taxes %} plus {{ rate }}% {{ taxname }} {% endblocktrans %} @@ -730,7 +730,7 @@ {% if fee.tax_rate %}
- {% blocktrans trimmed with rate=fee.tax_rate|floatformat:-2 taxname=fee.tax_rule.name|default:s_taxes %} + {% blocktrans trimmed with rate=fee.tax_rate|tax_rate_format taxname=fee.tax_rule.name|default:s_taxes %} incl. {{ rate }}% {{ taxname }} {% endblocktrans %} diff --git a/src/pretix/helpers/models.py b/src/pretix/helpers/models.py index 8e38141969..1c6c622a0a 100644 --- a/src/pretix/helpers/models.py +++ b/src/pretix/helpers/models.py @@ -23,6 +23,7 @@ import copy from django.core.files import File from django.db import models +from django.db.models.fields import DecimalField class Thumbnail(models.Model): @@ -54,3 +55,15 @@ def flatten_choices(choices): yield from label_or_nested else: yield value_or_group, label_or_nested + + +class NormalizedDecimalField(DecimalField): + """ + Variant of DecimalField that never outputs the trailing zeros, so we always have normalized decimals internally. + Use this only for fields where the trailing zeros are pointless (e.g. percentages), not for monetary amounts. + """ + + def from_db_value(self, value, expression, connection): + if value is not None: + value = value.normalize() + return value diff --git a/src/pretix/presale/templates/pretixpresale/event/fragment_addon_choice.html b/src/pretix/presale/templates/pretixpresale/event/fragment_addon_choice.html index 634765fe29..489b29772f 100644 --- a/src/pretix/presale/templates/pretixpresale/event/fragment_addon_choice.html +++ b/src/pretix/presale/templates/pretixpresale/event/fragment_addon_choice.html @@ -173,11 +173,11 @@ {% trans "incl. taxes" %} {% endif %} {% elif var.display_price.rate and var.display_price.gross and event.settings.display_net_prices %} - {% blocktrans trimmed with rate=var.display_price.rate|floatformat:-2 name=var.display_price.name %} + {% blocktrans trimmed with rate=var.display_price.rate|tax_rate_format name=var.display_price.name %} plus {{ rate }}% {{ name }} {% endblocktrans %} {% elif var.display_price.rate and var.display_price.gross %} - {% blocktrans trimmed with rate=var.display_price.rate|floatformat:-2 name=var.display_price.name %} + {% blocktrans trimmed with rate=var.display_price.rate|tax_rate_format name=var.display_price.name %} incl. {{ rate }}% {{ name }} {% endblocktrans %} {% endif %} @@ -313,11 +313,11 @@ {% trans "incl. taxes" %} {% endif %} {% elif item.display_price.rate and item.display_price.gross and event.settings.display_net_prices %} - {% blocktrans trimmed with rate=item.display_price.rate|floatformat:-2 name=item.display_price.name %} + {% blocktrans trimmed with rate=item.display_price.rate|tax_rate_format name=item.display_price.name %} plus {{ rate }}% {{ name }} {% endblocktrans %} {% elif item.display_price.rate and item.display_price.gross %} - {% blocktrans trimmed with rate=item.display_price.rate|floatformat:-2 name=item.display_price.name %} + {% blocktrans trimmed with rate=item.display_price.rate|tax_rate_format name=item.display_price.name %} incl. {{ rate }}% {{ name }} {% endblocktrans %} {% endif %} diff --git a/src/pretix/presale/templates/pretixpresale/event/fragment_cart.html b/src/pretix/presale/templates/pretixpresale/event/fragment_cart.html index b0ae79bdf5..92f388a521 100644 --- a/src/pretix/presale/templates/pretixpresale/event/fragment_cart.html +++ b/src/pretix/presale/templates/pretixpresale/event/fragment_cart.html @@ -357,7 +357,7 @@ {% if line.tax_rate and line.total %}
- {% blocktrans trimmed with rate=line.tax_rate|floatformat:-2 taxname=line.tax_rule.name|default:s_taxes %} + {% blocktrans trimmed with rate=line.tax_rate|tax_rate_format taxname=line.tax_rule.name|default:s_taxes %} plus {{ rate }}% {{ taxname }} {% endblocktrans %} @@ -367,7 +367,7 @@ {% if line.tax_rate and line.total %}
- {% blocktrans trimmed with rate=line.tax_rate|floatformat:-2 taxname=line.tax_rule.name|default:s_taxes %} + {% blocktrans trimmed with rate=line.tax_rate|tax_rate_format taxname=line.tax_rule.name|default:s_taxes %} incl. {{ rate }}% {{ taxname }} {% endblocktrans %} @@ -416,7 +416,7 @@ {% if fee.tax_rate %}
- {% blocktrans trimmed with rate=fee.tax_rate|floatformat:-2 taxname=fee.tax_rule.name|default:s_taxes %} + {% blocktrans trimmed with rate=fee.tax_rate|tax_rate_format taxname=fee.tax_rule.name|default:s_taxes %} plus {{ rate }}% {{ taxname }} {% endblocktrans %} @@ -426,7 +426,7 @@ {% if fee.tax_rate %}
- {% blocktrans trimmed with rate=fee.tax_rate|floatformat:-2 taxname=fee.tax_rule.name|default:s_taxes %} + {% blocktrans trimmed with rate=fee.tax_rate|tax_rate_format taxname=fee.tax_rule.name|default:s_taxes %} incl. {{ rate }}% {{ taxname }} {% endblocktrans %} diff --git a/src/pretix/presale/templates/pretixpresale/event/fragment_product_list.html b/src/pretix/presale/templates/pretixpresale/event/fragment_product_list.html index febcba9eb5..13daae66c3 100644 --- a/src/pretix/presale/templates/pretixpresale/event/fragment_product_list.html +++ b/src/pretix/presale/templates/pretixpresale/event/fragment_product_list.html @@ -207,13 +207,13 @@ {% endif %} {% elif var.display_price.rate and var.display_price.gross and event.settings.display_net_prices %} - {% blocktrans trimmed with rate=var.display_price.rate|floatformat:-2 name=var.display_price.name %} + {% blocktrans trimmed with rate=var.display_price.rate|tax_rate_format name=var.display_price.name %} plus {{ rate }}% {{ name }} {% endblocktrans %} {% elif var.display_price.rate and var.display_price.gross %} - {% blocktrans trimmed with rate=var.display_price.rate|floatformat:-2 name=var.display_price.name %} + {% blocktrans trimmed with rate=var.display_price.rate|tax_rate_format name=var.display_price.name %} incl. {{ rate }}% {{ name }} {% endblocktrans %} @@ -372,13 +372,13 @@ {% endif %} {% elif item.display_price.rate and item.display_price.gross and event.settings.display_net_prices %} - {% blocktrans trimmed with rate=item.display_price.rate|floatformat:-2 name=item.display_price.name %} + {% blocktrans trimmed with rate=item.display_price.rate|tax_rate_format name=item.display_price.name %} plus {{ rate }}% {{ name }} {% endblocktrans %} {% elif item.display_price.rate and item.display_price.gross %} - {% blocktrans trimmed with rate=item.display_price.rate|floatformat:-2 name=item.display_price.name %} + {% blocktrans trimmed with rate=item.display_price.rate|tax_rate_format name=item.display_price.name %} incl. {{ rate }}% {{ name }} {% endblocktrans %} diff --git a/src/pretix/presale/templates/pretixpresale/event/voucher.html b/src/pretix/presale/templates/pretixpresale/event/voucher.html index 317f2be7f9..72913c6a6b 100644 --- a/src/pretix/presale/templates/pretixpresale/event/voucher.html +++ b/src/pretix/presale/templates/pretixpresale/event/voucher.html @@ -201,13 +201,13 @@ {% endif %} {% elif var.display_price.rate and var.display_price.gross and event.settings.display_net_prices %} - {% blocktrans trimmed with rate=var.display_price.rate|floatformat:-2 name=var.display_price.name %} + {% blocktrans trimmed with rate=var.display_price.rate|tax_rate_format name=var.display_price.name %} plus {{ rate }}% {{ name }} {% endblocktrans %} {% elif var.display_price.rate and var.display_price.gross %} - {% blocktrans trimmed with rate=var.display_price.rate|floatformat:-2 name=var.display_price.name %} + {% blocktrans trimmed with rate=var.display_price.rate|tax_rate_format name=var.display_price.name %} incl. {{ rate }}% {{ name }} {% endblocktrans %} @@ -356,13 +356,13 @@ {% endif %} {% elif item.display_price.rate and item.display_price.gross and event.settings.display_net_prices %} - {% blocktrans trimmed with rate=item.display_price.rate|floatformat:-2 name=item.display_price.name %} + {% blocktrans trimmed with rate=item.display_price.rate|tax_rate_format name=item.display_price.name %} plus {{ rate }}% {{ name }} {% endblocktrans %} {% elif item.display_price.rate and item.display_price.gross %} - {% blocktrans trimmed with rate=item.display_price.rate|floatformat:-2 name=item.display_price.name %} + {% blocktrans trimmed with rate=item.display_price.rate|tax_rate_format name=item.display_price.name %} incl. {{ rate }}% {{ name }} {% endblocktrans %} diff --git a/src/pretix/static/jsi18n/en/djangojs.js b/src/pretix/static/jsi18n/en/djangojs.js index 5dee27a8f0..4d833a7135 100644 --- a/src/pretix/static/jsi18n/en/djangojs.js +++ b/src/pretix/static/jsi18n/en/djangojs.js @@ -1,5 +1,4 @@ - 'use strict'; { const globals = this; diff --git a/src/tests/presale/test_widget.py b/src/tests/presale/test_widget.py index 9b338ec915..9ab92b5710 100644 --- a/src/tests/presale/test_widget.py +++ b/src/tests/presale/test_widget.py @@ -189,8 +189,8 @@ class WidgetCartTest(CartTestMixin, TestCase): "current_unavailability_reason": None, "order_min": None, "max_price": None, - "price": {"gross": "23.00", "net": "19.33", "tax": "3.67", "name": "", "rate": "19.00", "includes_mixed_tax_rate": False}, - "suggested_price": {"gross": "23.00", "net": "19.33", "tax": "3.67", "name": "", "rate": "19.00", "includes_mixed_tax_rate": False}, + "price": {"gross": "23.00", "net": "19.33", "tax": "3.67", "name": "", "rate": "19", "includes_mixed_tax_rate": False}, + "suggested_price": {"gross": "23.00", "net": "19.33", "tax": "3.67", "name": "", "rate": "19", "includes_mixed_tax_rate": False}, "picture": None, "picture_fullsize": None, "has_variations": 0, @@ -226,9 +226,9 @@ class WidgetCartTest(CartTestMixin, TestCase): "id": self.shirt_red.pk, 'original_price': None, "price": {"gross": "14.00", "net": "11.76", "tax": "2.24", "name": "", - "rate": "19.00", "includes_mixed_tax_rate": False}, + "rate": "19", "includes_mixed_tax_rate": False}, "suggested_price": {"gross": "14.00", "net": "11.76", "tax": "2.24", "name": "", - "rate": "19.00", "includes_mixed_tax_rate": False}, + "rate": "19", "includes_mixed_tax_rate": False}, "description": None, "avail": [100, None], "order_max": 2, @@ -239,9 +239,9 @@ class WidgetCartTest(CartTestMixin, TestCase): "id": self.shirt_blue.pk, 'original_price': None, "price": {"gross": "12.00", "net": "10.08", "tax": "1.92", "name": "", - "rate": "19.00", "includes_mixed_tax_rate": False}, + "rate": "19", "includes_mixed_tax_rate": False}, "suggested_price": {"gross": "12.00", "net": "10.08", "tax": "1.92", "name": "", - "rate": "19.00", "includes_mixed_tax_rate": False}, + "rate": "19", "includes_mixed_tax_rate": False}, "description": None, "avail": [100, None], "order_max": 2, @@ -278,9 +278,9 @@ class WidgetCartTest(CartTestMixin, TestCase): "current_unavailability_reason": None, "order_min": None, "max_price": None, - "price": {"gross": "23.00", "net": "19.33", "tax": "3.67", "name": "", "rate": "19.00", + "price": {"gross": "23.00", "net": "19.33", "tax": "3.67", "name": "", "rate": "19", "includes_mixed_tax_rate": False}, - "suggested_price": {"gross": "23.00", "net": "19.33", "tax": "3.67", "name": "", "rate": "19.00", + "suggested_price": {"gross": "23.00", "net": "19.33", "tax": "3.67", "name": "", "rate": "19", "includes_mixed_tax_rate": False}, "picture": None, "picture_fullsize": None, @@ -343,9 +343,9 @@ class WidgetCartTest(CartTestMixin, TestCase): "id": self.shirt_red.pk, 'original_price': None, "price": {"gross": "14.00", "net": "11.76", "tax": "2.24", "name": "", - "rate": "19.00", "includes_mixed_tax_rate": False}, + "rate": "19", "includes_mixed_tax_rate": False}, "suggested_price": {"gross": "14.00", "net": "11.76", "tax": "2.24", "name": "", - "rate": "19.00", "includes_mixed_tax_rate": False}, + "rate": "19", "includes_mixed_tax_rate": False}, "description": None, "avail": [100, None], "order_max": 2, @@ -395,8 +395,8 @@ class WidgetCartTest(CartTestMixin, TestCase): "current_unavailability_reason": None, "order_min": None, "max_price": None, - "price": {"gross": "23.00", "net": "19.33", "tax": "3.67", "name": "", "rate": "19.00", "includes_mixed_tax_rate": False}, - "suggested_price": {"gross": "23.00", "net": "19.33", "tax": "3.67", "name": "", "rate": "19.00", "includes_mixed_tax_rate": False}, + "price": {"gross": "23.00", "net": "19.33", "tax": "3.67", "name": "", "rate": "19", "includes_mixed_tax_rate": False}, + "suggested_price": {"gross": "23.00", "net": "19.33", "tax": "3.67", "name": "", "rate": "19", "includes_mixed_tax_rate": False}, "picture": None, "picture_fullsize": None, "has_variations": 0, @@ -481,7 +481,7 @@ class WidgetCartTest(CartTestMixin, TestCase): 'gross': '14.00', 'net': '11.76', 'tax': '2.24', - 'rate': '19.00', + 'rate': '19', 'name': '', 'includes_mixed_tax_rate': False }, @@ -489,7 +489,7 @@ class WidgetCartTest(CartTestMixin, TestCase): 'gross': '14.00', 'net': '11.76', 'tax': '2.24', - 'rate': '19.00', + 'rate': '19', 'name': '', 'includes_mixed_tax_rate': False }, @@ -601,7 +601,7 @@ class WidgetCartTest(CartTestMixin, TestCase): "net": "19.52", "tax": "3.48", "name": "MIXED!", - "rate": "19.00", + "rate": "19", "includes_mixed_tax_rate": True }