diff --git a/src/pretix/base/decimal.py b/src/pretix/base/decimal.py
index 2e6ede00ef..883a08a251 100644
--- a/src/pretix/base/decimal.py
+++ b/src/pretix/base/decimal.py
@@ -1,5 +1,12 @@
from decimal import ROUND_HALF_UP, Decimal
+from django.conf import settings
-def round_decimal(dec):
+
+def round_decimal(dec, currency=None):
+ if currency:
+ places = settings.CURRENCY_PLACES.get(currency, 2)
+ return Decimal(dec).quantize(
+ Decimal('1') / 10 ** places, ROUND_HALF_UP
+ )
return Decimal(dec).quantize(Decimal('0.01'), ROUND_HALF_UP)
diff --git a/src/pretix/base/forms/__init__.py b/src/pretix/base/forms/__init__.py
index 0af8d1d8b7..1c15aa368d 100644
--- a/src/pretix/base/forms/__init__.py
+++ b/src/pretix/base/forms/__init__.py
@@ -11,16 +11,16 @@ from pretix.base.reldate import RelativeDateField, RelativeDateTimeField
from .validators import PlaceholderValidator # NOQA
-logger = logging.getLogger('pretix.plugins.ticketoutputpdf')
+logger = logging.getLogger(__name__)
class BaseI18nModelForm(i18nfield.forms.BaseI18nModelForm):
# compatibility shim for django-i18nfield library
def __init__(self, *args, **kwargs):
- event = kwargs.pop('event', None)
- if event:
- kwargs['locales'] = event.settings.get('locales')
+ self.event = kwargs.pop('event', None)
+ if self.event:
+ kwargs['locales'] = self.event.settings.get('locales')
super().__init__(*args, **kwargs)
@@ -32,9 +32,9 @@ class I18nFormSet(i18nfield.forms.I18nModelFormSet):
# compatibility shim for django-i18nfield library
def __init__(self, *args, **kwargs):
- event = kwargs.pop('event', None)
- if event:
- kwargs['locales'] = event.settings.get('locales')
+ self.event = kwargs.pop('event', None)
+ if self.event:
+ kwargs['locales'] = self.event.settings.get('locales')
super().__init__(*args, **kwargs)
diff --git a/src/pretix/base/i18n.py b/src/pretix/base/i18n.py
index f39935a6da..083115d610 100644
--- a/src/pretix/base/i18n.py
+++ b/src/pretix/base/i18n.py
@@ -12,6 +12,8 @@ from i18nfield.forms import I18nFormField # noqa
from i18nfield.strings import LazyI18nString # noqa
from i18nfield.utils import I18nJSONEncoder # noqa
+from pretix.base.templatetags.money import money_filter
+
class LazyDate:
def __init__(self, value):
@@ -24,6 +26,18 @@ class LazyDate:
return date_format(self.value, "SHORT_DATE_FORMAT")
+class LazyCurrencyNumber:
+ def __init__(self, value, currency):
+ self.value = value
+ self.currency = currency
+
+ def __format__(self, format_spec):
+ return self.__str__()
+
+ def __str__(self):
+ return money_filter(self.value, self.currency)
+
+
class LazyNumber:
def __init__(self, value, decimal_pos=2):
self.value = value
diff --git a/src/pretix/base/invoice.py b/src/pretix/base/invoice.py
index adb0e0ef66..5790b42208 100644
--- a/src/pretix/base/invoice.py
+++ b/src/pretix/base/invoice.py
@@ -24,6 +24,7 @@ from reportlab.platypus import (
from pretix.base.decimal import round_decimal
from pretix.base.models import Event, Invoice
from pretix.base.signals import register_invoice_renderers
+from pretix.base.templatetags.money import money_filter
class BaseInvoiceRenderer:
@@ -376,14 +377,14 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
Paragraph(line.description, self.stylesheet['Normal']),
"1",
localize(line.tax_rate) + " %",
- localize(line.net_value) + " " + self.invoice.event.currency,
- localize(line.gross_value) + " " + self.invoice.event.currency,
+ money_filter(line.net_value, self.invoice.event.currency),
+ money_filter(line.gross_value, self.invoice.event.currency),
))
else:
tdata.append((
Paragraph(line.description, self.stylesheet['Normal']),
"1",
- localize(line.gross_value) + " " + self.invoice.event.currency,
+ money_filter(line.gross_value, self.invoice.event.currency),
))
taxvalue_map[line.tax_rate, line.tax_name] += line.tax_value
grossvalue_map[line.tax_rate, line.tax_name] += line.gross_value
@@ -391,12 +392,12 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
if has_taxes:
tdata.append([
- pgettext('invoice', 'Invoice total'), '', '', '', localize(total) + " " + self.invoice.event.currency
+ pgettext('invoice', 'Invoice total'), '', '', '', money_filter(total, self.invoice.event.currency)
])
colwidths = [a * doc.width for a in (.50, .05, .15, .15, .15)]
else:
tdata.append([
- pgettext('invoice', 'Invoice total'), '', localize(total) + " " + self.invoice.event.currency
+ pgettext('invoice', 'Invoice total'), '', money_filter(total, self.invoice.event.currency)
])
colwidths = [a * doc.width for a in (.65, .05, .30)]
@@ -436,9 +437,9 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
tax = taxvalue_map[idx]
tdata.append([
localize(rate) + " % " + name,
- localize(gross - tax) + " " + self.invoice.event.currency,
- localize(gross) + " " + self.invoice.event.currency,
- localize(tax) + " " + self.invoice.event.currency,
+ money_filter(gross - tax, self.invoice.event.currency),
+ money_filter(gross, self.invoice.event.currency),
+ money_filter(tax, self.invoice.event.currency),
''
])
diff --git a/src/pretix/base/models/invoices.py b/src/pretix/base/models/invoices.py
index 249d3e0ccf..9cb53a4937 100644
--- a/src/pretix/base/models/invoices.py
+++ b/src/pretix/base/models/invoices.py
@@ -83,6 +83,7 @@ class Invoice(models.Model):
foreign_currency_display = models.CharField(max_length=50, null=True, blank=True)
foreign_currency_rate = models.DecimalField(decimal_places=4, max_digits=10, null=True, blank=True)
foreign_currency_rate_date = models.DateField(null=True, blank=True)
+
file = models.FileField(null=True, blank=True, upload_to=invoice_filename)
internal_reference = models.TextField(blank=True)
diff --git a/src/pretix/base/models/tax.py b/src/pretix/base/models/tax.py
index addb305779..349abf6a40 100644
--- a/src/pretix/base/models/tax.py
+++ b/src/pretix/base/models/tax.py
@@ -8,6 +8,7 @@ from i18nfield.fields import I18nCharField
from pretix.base.decimal import round_decimal
from pretix.base.models.base import LoggedModel
+from pretix.base.templatetags.money import money_filter
class TaxedPrice:
@@ -23,6 +24,13 @@ class TaxedPrice:
def __repr__(self):
return '{} + {}% = {}'.format(localize(self.net), localize(self.rate), localize(self.gross))
+ def print(self, currency):
+ return '{} + {}% = {}'.format(
+ money_filter(self.net, currency),
+ localize(self.rate),
+ money_filter(self.gross, currency)
+ )
+
TAXED_ZERO = TaxedPrice(
gross=Decimal('0.00'),
@@ -129,10 +137,12 @@ class TaxRule(LoggedModel):
if base_price_is == 'gross':
gross = base_price
- net = gross - round_decimal(base_price * (1 - 100 / (100 + self.rate)))
+ net = round_decimal(gross - (base_price * (1 - 100 / (100 + self.rate))),
+ self.event.currency if self.event else None)
elif base_price_is == 'net':
net = base_price
- gross = round_decimal(net * (1 + self.rate / 100))
+ gross = round_decimal((net * (1 + self.rate / 100)),
+ self.event.currency if self.event else None)
else:
raise ValueError('Unknown base price type: {}'.format(base_price_is))
diff --git a/src/pretix/base/models/vouchers.py b/src/pretix/base/models/vouchers.py
index 2d0942674c..390c6d8390 100644
--- a/src/pretix/base/models/vouchers.py
+++ b/src/pretix/base/models/vouchers.py
@@ -1,4 +1,4 @@
-from decimal import Decimal
+from decimal import ROUND_HALF_UP, Decimal
from django.conf import settings
from django.core.exceptions import ValidationError
@@ -368,9 +368,15 @@ class Voucher(LoggedModel):
"""
if self.value is not None:
if self.price_mode == 'set':
- return self.value
+ p = self.value
elif self.price_mode == 'subtract':
- return max(original_price - self.value, Decimal('0.00'))
+ p = max(original_price - self.value, Decimal('0.00'))
elif self.price_mode == 'percent':
- return round_decimal(original_price * (Decimal('100.00') - self.value) / Decimal('100.00'))
+ p = round_decimal(original_price * (Decimal('100.00') - self.value) / Decimal('100.00'))
+ else:
+ p = original_price
+ places = settings.CURRENCY_PLACES.get(self.event.currency, 2)
+ if places < 2:
+ return p.quantize(Decimal('1') / 10 ** places, ROUND_HALF_UP)
+ return p
return original_price
diff --git a/src/pretix/base/notifications.py b/src/pretix/base/notifications.py
index 8e937849e5..021d1b4d32 100644
--- a/src/pretix/base/notifications.py
+++ b/src/pretix/base/notifications.py
@@ -2,11 +2,12 @@ import logging
from collections import OrderedDict, namedtuple
from django.dispatch import receiver
-from django.utils.formats import date_format, localize
+from django.utils.formats import date_format
from django.utils.translation import ugettext_lazy as _
from pretix.base.models import Event, LogEntry
from pretix.base.signals import register_notification_types
+from pretix.base.templatetags.money import money_filter
from pretix.helpers.urls import build_absolute_uri
logger = logging.getLogger(__name__)
@@ -174,7 +175,7 @@ class ParametrizedOrderNotificationType(NotificationType):
url=order_url
)
n.add_attribute(_('Order code'), order.code)
- n.add_attribute(_('Order total'), '{} {}'.format(localize(order.total), logentry.event.currency))
+ n.add_attribute(_('Order total'), money_filter(order.total, logentry.event.currency))
n.add_attribute(_('Order date'), date_format(order.datetime, 'SHORT_DATETIME_FORMAT'))
n.add_attribute(_('Order status'), order.get_status_display())
n.add_attribute(_('Order positions'), str(order.positions.count()))
diff --git a/src/pretix/base/payment.py b/src/pretix/base/payment.py
index af9754098f..8a0b6aa1d6 100644
--- a/src/pretix/base/payment.py
+++ b/src/pretix/base/payment.py
@@ -1,10 +1,11 @@
import logging
from collections import OrderedDict
-from decimal import Decimal
+from decimal import ROUND_HALF_UP, Decimal
from typing import Any, Dict, Union
import pytz
from django import forms
+from django.conf import settings
from django.contrib import messages
from django.dispatch import receiver
from django.forms import Form
@@ -15,11 +16,11 @@ from django.utils.translation import pgettext_lazy, ugettext_lazy as _
from i18nfield.forms import I18nFormField, I18nTextarea
from i18nfield.strings import LazyI18nString
-from pretix.base.decimal import round_decimal
from pretix.base.models import CartPosition, Event, Order, Quota
from pretix.base.reldate import RelativeDateField, RelativeDateWrapper
from pretix.base.settings import SettingsSandbox
from pretix.base.signals import register_payment_providers
+from pretix.helpers.money import DecimalTextInput
from pretix.presale.views import get_cart_total
from pretix.presale.views.cart import get_or_create_cart_id
@@ -91,10 +92,15 @@ class BasePaymentProvider:
fee_abs = self.settings.get('_fee_abs', as_type=Decimal, default=0)
fee_percent = self.settings.get('_fee_percent', as_type=Decimal, default=0)
fee_reverse_calc = self.settings.get('_fee_reverse_calc', as_type=bool, default=True)
+ places = settings.CURRENCY_PLACES.get(self.event.currency, 2)
if fee_reverse_calc:
- return round_decimal((price + fee_abs) * (1 / (1 - fee_percent / 100)) - price)
+ return ((price + fee_abs) * (1 / (1 - fee_percent / 100)) - price).quantize(
+ Decimal('1') / 10 ** places, ROUND_HALF_UP
+ )
else:
- return round_decimal(price * fee_percent / 100) + fee_abs
+ return (price * fee_percent / 100 + fee_abs).quantize(
+ Decimal('1') / 10 ** places, ROUND_HALF_UP
+ )
@property
def verbose_name(self) -> str:
@@ -156,6 +162,7 @@ class BasePaymentProvider:
.. WARNING:: It is highly discouraged to alter the ``_enabled`` field of the default
implementation.
"""
+ places = settings.CURRENCY_PLACES.get(self.event.currency, 2)
return OrderedDict([
('_enabled',
forms.BooleanField(
@@ -166,7 +173,10 @@ class BasePaymentProvider:
forms.DecimalField(
label=_('Additional fee'),
help_text=_('Absolute value'),
- required=False
+ localize=True,
+ required=False,
+ decimal_places=places,
+ widget=DecimalTextInput(places=places)
)),
('_fee_percent',
forms.DecimalField(
@@ -174,7 +184,8 @@ class BasePaymentProvider:
help_text=_('Percentage of the order total. Note that this percentage will currently only '
'be calculated on the summed price of sold tickets, not on other fees like e.g. shipping '
'fees, if there are any.'),
- required=False
+ localize=True,
+ required=False,
)),
('_availability_date',
RelativeDateField(
diff --git a/src/pretix/base/services/orders.py b/src/pretix/base/services/orders.py
index d2ef97529b..8d77c38c74 100644
--- a/src/pretix/base/services/orders.py
+++ b/src/pretix/base/services/orders.py
@@ -17,7 +17,7 @@ from django.utils.timezone import make_aware, now
from django.utils.translation import ugettext as _
from pretix.base.i18n import (
- LazyDate, LazyLocaleException, LazyNumber, language,
+ LazyCurrencyNumber, LazyDate, LazyLocaleException, LazyNumber, language,
)
from pretix.base.models import (
CartPosition, Event, Item, ItemVariation, Order, OrderPosition, Quota,
@@ -504,6 +504,8 @@ def _create_order(event: Event, email: str, positions: List[CartPosition], now_d
for fee in fees:
fee.order = order
fee._calculate_tax()
+ if not fee.tax_rule.pk:
+ fee.tax_rule = None # TODO: deprecate
fee.save()
OrderPosition.transform_cart_positions(positions, order)
@@ -568,6 +570,7 @@ def _perform_order(event: str, payment_provider: str, position_ids: List[str],
email_context = {
'total': LazyNumber(order.total),
'currency': event.currency,
+ 'total_with_currency': LazyCurrencyNumber(order.total, event.currency),
'date': LazyDate(order.expires),
'event': event.name,
'url': build_absolute_uri(event, 'presale:event.order', kwargs={
diff --git a/src/pretix/base/services/pricing.py b/src/pretix/base/services/pricing.py
index ec7a7117d6..f63247924e 100644
--- a/src/pretix/base/services/pricing.py
+++ b/src/pretix/base/services/pricing.py
@@ -1,5 +1,6 @@
from decimal import Decimal
+from pretix.base.decimal import round_decimal
from pretix.base.models import (
AbstractPosition, InvoiceAddress, Item, ItemAddOn, ItemVariation, Voucher,
)
@@ -59,4 +60,8 @@ def get_price(item: Item, variation: ItemVariation = None,
price.gross = price.net
price.name = ''
+ price.gross = round_decimal(price.gross, item.event.currency)
+ price.net = round_decimal(price.net, item.event.currency)
+ price.tax = price.gross - price.net
+
return price
diff --git a/src/pretix/base/settings.py b/src/pretix/base/settings.py
index 62ce482b27..6215a5c7f5 100644
--- a/src/pretix/base/settings.py
+++ b/src/pretix/base/settings.py
@@ -276,7 +276,7 @@ Your {event} team"""))
'default': LazyI18nString.from_gettext(ugettext_noop("""Hello,
we successfully received your order for {event} with a total value
-of {total} {currency}. Please complete your payment before {date}.
+of {total_with_currency}. Please complete your payment before {date}.
{payment_info}
diff --git a/src/pretix/base/templatetags/money.py b/src/pretix/base/templatetags/money.py
new file mode 100644
index 0000000000..29edc6f98a
--- /dev/null
+++ b/src/pretix/base/templatetags/money.py
@@ -0,0 +1,55 @@
+from decimal import ROUND_HALF_UP, Decimal
+
+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 translation
+
+register = template.Library()
+
+
+@register.filter("money")
+def money_filter(value: Decimal, arg='', hide_currency=False):
+ if isinstance(value, float) or isinstance(value, int):
+ value = Decimal(value)
+ if not isinstance(value, Decimal):
+ raise TypeError("Invalid data type passed to money filter: %r" % type(value))
+ if not arg:
+ raise ValueError("No currency passed.")
+
+ places = settings.CURRENCY_PLACES.get(arg, 2)
+ rounded = value.quantize(Decimal('1') / 10 ** places, ROUND_HALF_UP)
+ if places < 2 and rounded != value:
+ places = 2
+ if hide_currency:
+ return floatformat(value, places)
+
+ try:
+ if rounded != value:
+ # We display decimal places even if we shouldn't for this currency if rounding
+ # would make the numbers incorrect. If this branch executes, it's likely a bug in
+ # pretix, but we won't show wrong numbers!
+ return '{} {}'.format(
+ arg,
+ floatformat(value, 2)
+ )
+ return format_currency(value, arg, locale=translation.get_language())
+ except:
+ return '{} {}'.format(
+ arg,
+ floatformat(value, places)
+ )
+
+
+@register.filter("money_numberfield")
+def money_numberfield_filter(value: Decimal, arg=''):
+ if isinstance(value, float) or isinstance(value, int):
+ value = Decimal(value)
+ if not isinstance(value, Decimal):
+ raise TypeError("Invalid data type passed to money filter: %r" % type(value))
+ if not arg:
+ raise ValueError("No currency passed.")
+
+ places = settings.CURRENCY_PLACES.get(arg, 2)
+ return str(value.quantize(Decimal('1') / 10 ** places, ROUND_HALF_UP))
diff --git a/src/pretix/control/forms/event.py b/src/pretix/control/forms/event.py
index 649677e642..046909636d 100644
--- a/src/pretix/control/forms/event.py
+++ b/src/pretix/control/forms/event.py
@@ -666,10 +666,10 @@ class MailSettingsForm(SettingsForm):
label=_("Text"),
required=False,
widget=I18nTextarea,
- help_text=_("Available placeholders: {event}, {total}, {currency}, {date}, {payment_info}, {url}, "
- "{invoice_name}, {invoice_company}"),
- validators=[PlaceholderValidator(['{event}', '{total}', '{currency}', '{date}', '{payment_info}',
- '{url}', '{invoice_name}', '{invoice_company}'])]
+ help_text=_("Available placeholders: {event}, {total_with_currency}, {total}, {currency}, {date}, "
+ "{payment_info}, {url}, {invoice_name}, {invoice_company}"),
+ validators=[PlaceholderValidator(['{event}', '{total_with_currency}', '{total}', '{currency}', '{date}',
+ '{payment_info}', '{url}', '{invoice_name}', '{invoice_company}'])]
)
mail_text_order_paid = I18nFormField(
label=_("Text"),
diff --git a/src/pretix/control/forms/item.py b/src/pretix/control/forms/item.py
index 4e72a955fc..20ff8eea23 100644
--- a/src/pretix/control/forms/item.py
+++ b/src/pretix/control/forms/item.py
@@ -17,6 +17,7 @@ from pretix.base.models import (
from pretix.base.models.items import ItemAddOn
from pretix.control.forms import SplitDateTimePickerWidget
from pretix.control.forms.widgets import Select2
+from pretix.helpers.money import change_decimal_field
class CategoryForm(I18nModelForm):
@@ -159,6 +160,7 @@ class ItemCreateForm(I18nModelForm):
self.fields['category'].queryset = self.instance.event.categories.all()
self.fields['tax_rule'].queryset = self.instance.event.tax_rules.all()
+ change_decimal_field(self.fields['default_price'], self.instance.event.currency)
self.fields['tax_rule'].empty_label = _('No taxation')
self.fields['copy_from'] = forms.ModelChoiceField(
label=_("Copy product information"),
@@ -292,6 +294,7 @@ class ItemUpdateForm(I18nModelForm):
'over 65. This ticket includes access to all parts of the event, except the VIP '
'area.'
)
+ change_decimal_field(self.fields['default_price'], self.event.currency)
class Meta:
model = Item
@@ -345,8 +348,29 @@ class ItemVariationsFormSet(I18nFormSet):
return False
return form.cleaned_data.get(DELETION_FIELD_NAME, False)
+ def _construct_form(self, i, **kwargs):
+ kwargs['event'] = self.event
+ return super()._construct_form(i, **kwargs)
+
+ @property
+ def empty_form(self):
+ self.is_valid()
+ form = self.form(
+ auto_id=self.auto_id,
+ prefix=self.add_prefix('__prefix__'),
+ empty_permitted=True,
+ locales=self.locales,
+ event=self.event
+ )
+ self.add_fields(form, None)
+ return form
+
class ItemVariationForm(I18nModelForm):
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ change_decimal_field(self.fields['default_price'], self.event.currency)
+
class Meta:
model = ItemVariation
localized_fields = '__all__'
@@ -399,7 +423,6 @@ class ItemAddOnsFormSet(I18nFormSet):
class ItemAddOnForm(I18nModelForm):
def __init__(self, *args, **kwargs):
- self.event = kwargs.pop('event')
super().__init__(*args, **kwargs)
self.fields['addon_category'].queryset = self.event.categories.all()
diff --git a/src/pretix/control/forms/orders.py b/src/pretix/control/forms/orders.py
index bcd04c1119..652c155173 100644
--- a/src/pretix/control/forms/orders.py
+++ b/src/pretix/control/forms/orders.py
@@ -2,7 +2,6 @@ from django import forms
from django.conf import settings
from django.core.exceptions import ValidationError
from django.db import models
-from django.utils.formats import localize
from django.utils.timezone import now
from django.utils.translation import pgettext_lazy, ugettext_lazy as _
@@ -12,6 +11,8 @@ from pretix.base.models import (
)
from pretix.base.models.event import SubEvent
from pretix.base.services.pricing import get_price
+from pretix.base.templatetags.money import money_filter
+from pretix.helpers.money import change_decimal_field
class ExtendForm(I18nModelForm):
@@ -76,8 +77,8 @@ class SubEventChoiceField(forms.ModelChoiceField):
p = get_price(self.instance.item, self.instance.variation,
voucher=self.instance.voucher,
subevent=obj)
- return '{} – {} ({} {})'.format(obj.name, obj.get_date_range_display(),
- p, self.instance.order.event.currency)
+ return '{} – {} ({})'.format(obj.name, obj.get_date_range_display(),
+ money_filter(p, self.instance.order.event.currency))
class OtherOperationsForm(forms.Form):
@@ -120,6 +121,7 @@ class OrderPositionAddForm(forms.Form):
price = forms.DecimalField(
required=False,
max_digits=10, decimal_places=2,
+ localize=True,
label=_('Gross price'),
help_text=_("Including taxes, if any. Keep empty for the product's default price")
)
@@ -149,10 +151,10 @@ class OrderPositionAddForm(forms.Form):
for v in variations:
p = get_price(i, v, invoice_address=ia)
choices.append(('%d-%d' % (i.pk, v.pk),
- '%s – %s (%s %s)' % (pname, v.value, p, order.event.currency)))
+ '%s – %s (%s)' % (pname, v.value, p.print(order.event.currency))))
else:
p = get_price(i, invoice_address=ia)
- choices.append((str(i.pk), '%s (%s %s)' % (pname, p, order.event.currency)))
+ choices.append((str(i.pk), '%s (%s)' % (pname, p.print(order.event.currency))))
self.fields['itemvar'].choices = choices
if ItemAddOn.objects.filter(base_item__event=order.event).exists():
self.fields['addon_to'].queryset = order.positions.filter(addon_to__isnull=True).select_related(
@@ -165,6 +167,7 @@ class OrderPositionAddForm(forms.Form):
self.fields['subevent'].queryset = order.event.subevents.all()
else:
del self.fields['subevent']
+ change_decimal_field(self.fields['price'], order.event.currency)
class OrderPositionChangeForm(forms.Form):
@@ -178,6 +181,7 @@ class OrderPositionChangeForm(forms.Form):
price = forms.DecimalField(
required=False,
max_digits=10, decimal_places=2,
+ localize=True,
label=_('New price (gross)')
)
operation = forms.ChoiceField(
@@ -236,14 +240,13 @@ class OrderPositionChangeForm(forms.Form):
p = get_price(i, v, voucher=instance.voucher, subevent=instance.subevent,
invoice_address=ia)
choices.append(('%d-%d' % (i.pk, v.pk),
- '%s – %s (%s %s)' % (pname, v.value, localize(p),
- instance.order.event.currency)))
+ '%s – %s (%s)' % (pname, v.value, p.print(instance.order.event.currency))))
else:
p = get_price(i, None, voucher=instance.voucher, subevent=instance.subevent,
invoice_address=ia)
- choices.append((str(i.pk), '%s (%s %s)' % (pname, localize(p),
- instance.order.event.currency)))
+ choices.append((str(i.pk), '%s (%s)' % (pname, p.print(instance.order.event.currency))))
self.fields['itemvar'].choices = choices
+ change_decimal_field(self.fields['price'], instance.order.event.currency)
def clean(self):
if self.cleaned_data.get('operation') == 'price' and not self.cleaned_data.get('price', '') != '':
diff --git a/src/pretix/control/forms/subevents.py b/src/pretix/control/forms/subevents.py
index e857d8b5fe..a710a2bc0e 100644
--- a/src/pretix/control/forms/subevents.py
+++ b/src/pretix/control/forms/subevents.py
@@ -5,7 +5,9 @@ from i18nfield.forms import I18nInlineFormSet
from pretix.base.forms import I18nModelForm
from pretix.base.models.event import SubEvent, SubEventMetaValue
from pretix.base.models.items import SubEventItem
+from pretix.base.templatetags.money import money_filter
from pretix.control.forms import SplitDateTimePickerWidget
+from pretix.helpers.money import change_decimal_field
class SubEventForm(I18nModelForm):
@@ -49,32 +51,35 @@ class SubEventItemOrVariationFormMixin:
self.item = kwargs.pop('item')
self.variation = kwargs.pop('variation', None)
super().__init__(*args, **kwargs)
+ change_decimal_field(self.fields['price'], self.item.event.currency)
class SubEventItemForm(SubEventItemOrVariationFormMixin, forms.ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
- self.fields['price'].widget.attrs['placeholder'] = '{} {}'.format(
- self.item.default_price, self.item.event.currency
- )
+ self.fields['price'].widget.attrs['placeholder'] = money_filter(self.item.default_price, self.item.event.currency, hide_currency=True)
self.fields['price'].label = str(self.item.name)
class Meta:
model = SubEventItem
fields = ['price']
+ widgets = {
+ 'price': forms.TextInput
+ }
class SubEventItemVariationForm(SubEventItemOrVariationFormMixin, forms.ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
- self.fields['price'].widget.attrs['placeholder'] = '{} {}'.format(
- self.variation.price, self.item.event.currency
- )
+ self.fields['price'].widget.attrs['placeholder'] = money_filter(self.variation.price, self.item.event.currency, hide_currency=True)
self.fields['price'].label = '{} – {}'.format(str(self.item.name), self.variation.value)
class Meta:
model = SubEventItem
fields = ['price']
+ widgets = {
+ 'price': forms.TextInput
+ }
class QuotaFormSet(I18nInlineFormSet):
diff --git a/src/pretix/control/logdisplay.py b/src/pretix/control/logdisplay.py
index 36cfed17bf..b5ac30ce86 100644
--- a/src/pretix/control/logdisplay.py
+++ b/src/pretix/control/logdisplay.py
@@ -4,7 +4,6 @@ from decimal import Decimal
import dateutil.parser
import pytz
from django.dispatch import receiver
-from django.utils import formats
from django.utils.formats import date_format
from django.utils.translation import pgettext_lazy, ugettext_lazy as _
from i18nfield.strings import LazyI18nString
@@ -13,6 +12,7 @@ from pretix.base.models import (
CheckinList, Event, ItemVariation, LogEntry, OrderPosition,
)
from pretix.base.signals import logentry_display
+from pretix.base.templatetags.money import money_filter
OVERVIEW_BLACKLIST = [
'pretix.plugins.sendmail.order.email.sent'
@@ -30,42 +30,38 @@ def _display_order_changed(event: Event, logentry: LogEntry):
new_item = str(event.items.get(pk=data['new_item']))
if data['new_variation']:
new_item += ' - ' + str(ItemVariation.objects.get(item__event=event, pk=data['new_variation']))
- return text + ' ' + _('Position #{posid}: {old_item} ({old_price} {currency}) changed '
- 'to {new_item} ({new_price} {currency}).').format(
+ return text + ' ' + _('Position #{posid}: {old_item} ({old_price}) changed '
+ 'to {new_item} ({new_price}).').format(
posid=data.get('positionid', '?'),
old_item=old_item, new_item=new_item,
- old_price=formats.localize(Decimal(data['old_price'])),
- new_price=formats.localize(Decimal(data['new_price'])),
- currency=event.currency
+ old_price=money_filter(Decimal(data['old_price']), event.currency),
+ new_price=money_filter(Decimal(data['new_price']), event.currency),
)
elif logentry.action_type == 'pretix.event.order.changed.subevent':
old_se = str(event.subevents.get(pk=data['old_subevent']))
new_se = str(event.subevents.get(pk=data['new_subevent']))
- return text + ' ' + _('Position #{posid}: Event date "{old_event}" ({old_price} {currency}) changed '
- 'to "{new_event}" ({new_price} {currency}).').format(
+ return text + ' ' + _('Position #{posid}: Event date "{old_event}" ({old_price}) changed '
+ 'to "{new_event}" ({new_price}).').format(
posid=data.get('positionid', '?'),
old_event=old_se, new_event=new_se,
- old_price=formats.localize(Decimal(data['old_price'])),
- new_price=formats.localize(Decimal(data['new_price'])),
- currency=event.currency
+ old_price=money_filter(Decimal(data['old_price']), event.currency),
+ new_price=money_filter(Decimal(data['new_price']), event.currency),
)
elif logentry.action_type == 'pretix.event.order.changed.price':
- return text + ' ' + _('Price of position #{posid} changed from {old_price} {currency} '
- 'to {new_price} {currency}.').format(
+ return text + ' ' + _('Price of position #{posid} changed from {old_price} '
+ 'to {new_price}.').format(
posid=data.get('positionid', '?'),
- old_price=formats.localize(Decimal(data['old_price'])),
- new_price=formats.localize(Decimal(data['new_price'])),
- currency=event.currency
+ old_price=money_filter(Decimal(data['old_price']), event.currency),
+ new_price=money_filter(Decimal(data['new_price']), event.currency),
)
elif logentry.action_type == 'pretix.event.order.changed.cancel':
old_item = str(event.items.get(pk=data['old_item']))
if data['old_variation']:
old_item += ' - ' + str(ItemVariation.objects.get(pk=data['old_variation']))
- return text + ' ' + _('Position #{posid} ({old_item}, {old_price} {currency}) removed.').format(
+ return text + ' ' + _('Position #{posid} ({old_item}, {old_price}) removed.').format(
posid=data.get('positionid', '?'),
old_item=old_item,
- old_price=formats.localize(Decimal(data['old_price'])),
- currency=event.currency
+ old_price=money_filter(Decimal(data['old_price']), event.currency),
)
elif logentry.action_type == 'pretix.event.order.changed.add':
item = str(event.items.get(pk=data['item']))
@@ -73,30 +69,27 @@ def _display_order_changed(event: Event, logentry: LogEntry):
item += ' - ' + str(ItemVariation.objects.get(item__event=event, pk=data['variation']))
if data['addon_to']:
addon_to = OrderPosition.objects.get(order__event=event, pk=data['addon_to'])
- return text + ' ' + _('Position #{posid} created: {item} ({price} {currency}) as an add-on to '
+ return text + ' ' + _('Position #{posid} created: {item} ({price}) as an add-on to '
'position #{addon_to}.').format(
posid=data.get('positionid', '?'),
item=item, addon_to=addon_to.positionid,
- price=formats.localize(Decimal(data['price'])),
- currency=event.currency
+ price=money_filter(Decimal(data['price']), event.currency),
)
else:
- return text + ' ' + _('Position #{posid} created: {item} ({price} {currency}).').format(
+ return text + ' ' + _('Position #{posid} created: {item} ({price}).').format(
posid=data.get('positionid', '?'),
item=item,
- price=formats.localize(Decimal(data['price'])),
- currency=event.currency
+ price=money_filter(Decimal(data['price']), event.currency),
)
elif logentry.action_type == 'pretix.event.order.changed.split':
old_item = str(event.items.get(pk=data['old_item']))
if data['old_variation']:
old_item += ' - ' + str(ItemVariation.objects.get(pk=data['old_variation']))
- return text + ' ' + _('Position #{posid} ({old_item}, {old_price} {currency}) split into new order: {order}').format(
+ return text + ' ' + _('Position #{posid} ({old_item}, {old_price}) split into new order: {order}').format(
old_item=old_item,
posid=data.get('positionid', '?'),
order=data['new_order'],
- old_price=formats.localize(Decimal(data['old_price'])),
- currency=event.currency
+ old_price=money_filter(Decimal(data['old_price']), event.currency),
)
elif logentry.action_type == 'pretix.event.order.changed.split_from':
return _('This order has been created by splitting the order {order}').format(
diff --git a/src/pretix/control/templates/pretixcontrol/order/index.html b/src/pretix/control/templates/pretixcontrol/order/index.html
index fd73aed5c0..3bed87e0c0 100644
--- a/src/pretix/control/templates/pretixcontrol/order/index.html
+++ b/src/pretix/control/templates/pretixcontrol/order/index.html
@@ -2,6 +2,7 @@
{% load i18n %}
{% load bootstrap3 %}
{% load eventurl %}
+{% load money %}
{% load safelink %}
{% load eventsignal %}
{% block title %}
@@ -257,7 +258,7 @@
{% if event.settings.display_net_prices %}
- {{ event.currency }} {{ line.net_price|floatformat:2 }}
+ {{ line.net_price|money:event.currency }}
{% if line.tax_rate %}
@@ -267,7 +268,7 @@
{% endif %}
{% else %}
- {{ event.currency }} {{ line.price|floatformat:2 }}
+ {{ line.price|money:event.currency }}
{% if line.tax_rate and line.price %}
@@ -291,7 +292,7 @@
{% if event.settings.display_net_prices %}
- {{ event.currency }} {{ fee.net_value|floatformat:2 }}
+ {{ fee.net_value|money:event.currency }}
{% if fee.tax_rate %}
@@ -301,7 +302,7 @@
{% endif %}
{% else %}
- {{ event.currency }} {{ fee.value|floatformat:2 }}
+ {{ fee.value|money:event.currency }}
{% if fee.tax_rate %}
@@ -321,7 +322,7 @@
{% trans "Net total" %}
- {{ event.currency }} {{ items.net_total|floatformat:2 }}
+ {{ items.net_total|money:event.currency }}
@@ -330,7 +331,7 @@
{% trans "Taxes" %}
- {{ event.currency }} {{ items.tax_total|floatformat:2 }}
+ {{ items.tax_total|money:event.currency }}
@@ -340,7 +341,7 @@
{% trans "Total" %}
- {{ event.currency }} {{ items.total|floatformat:2 }}
+ {{ items.total|money:event.currency }}
diff --git a/src/pretix/control/templates/pretixcontrol/orders/index.html b/src/pretix/control/templates/pretixcontrol/orders/index.html
index 6a91e784e1..e8a023981c 100644
--- a/src/pretix/control/templates/pretixcontrol/orders/index.html
+++ b/src/pretix/control/templates/pretixcontrol/orders/index.html
@@ -2,6 +2,7 @@
{% load i18n %}
{% load eventurl %}
{% load urlreplace %}
+{% load money %}
{% load bootstrap3 %}
{% block title %}{% trans "Orders" %}{% endblock %}
{% block content %}
@@ -114,7 +115,7 @@
{% endif %}
{{ o.datetime|date:"SHORT_DATETIME_FORMAT" }} |
- {{ o.total|floatformat:2 }} {{ request.event.currency }} |
+ {{ o.total|money:request.event.currency }} |
{{ o.pcnt }} |
{% include "pretixcontrol/orders/fragment_order_status.html" with order=o %} |
diff --git a/src/pretix/control/templates/pretixcontrol/orders/overview.html b/src/pretix/control/templates/pretixcontrol/orders/overview.html
index 6224dccdb6..a607ee1878 100644
--- a/src/pretix/control/templates/pretixcontrol/orders/overview.html
+++ b/src/pretix/control/templates/pretixcontrol/orders/overview.html
@@ -50,12 +50,12 @@
{% if tup.0 %}
| {{ tup.0.name }} |
- {{ tup.0.num_canceled|togglesum }} |
- {{ tup.0.num_refunded|togglesum }} |
- {{ tup.0.num_expired|togglesum }} |
- {{ tup.0.num_pending|togglesum }} |
- {{ tup.0.num_paid|togglesum }} |
- {{ tup.0.num_total|togglesum }} |
+ {{ tup.0.num_canceled|togglesum:request.event.currency }} |
+ {{ tup.0.num_refunded|togglesum:request.event.currency }} |
+ {{ tup.0.num_expired|togglesum:request.event.currency }} |
+ {{ tup.0.num_pending|togglesum:request.event.currency }} |
+ {{ tup.0.num_paid|togglesum:request.event.currency }} |
+ {{ tup.0.num_total|togglesum:request.event.currency }} |
{% endif %}
{% for item in tup.1 %}
@@ -63,43 +63,43 @@
{{ item.name }} |
- {{ item.num_canceled|togglesum }}
+ {{ item.num_canceled|togglesum:request.event.currency }}
|
- {{ item.num_refunded|togglesum }}
+ {{ item.num_refunded|togglesum:request.event.currency }}
|
- {{ item.num_expired|togglesum }}
+ {{ item.num_expired|togglesum:request.event.currency }}
|
- {{ item.num_pending|togglesum }}
+ {{ item.num_pending|togglesum:request.event.currency }}
|
- {{ item.num_paid|togglesum }}
+ {{ item.num_paid|togglesum:request.event.currency }}
|
- {{ item.num_total|togglesum }}
+ {{ item.num_total|togglesum:request.event.currency }}
|
{% if item.has_variations %}
{% for var in item.all_variations %}
| {{ var }} |
- {{ var.num_canceled|togglesum }} |
- {{ var.num_refunded|togglesum }} |
- {{ var.num_expired|togglesum }} |
- {{ var.num_pending|togglesum }} |
- {{ var.num_paid|togglesum }} |
- {{ var.num_total|togglesum }} |
+ {{ var.num_canceled|togglesum:request.event.currency }} |
+ {{ var.num_refunded|togglesum:request.event.currency }} |
+ {{ var.num_expired|togglesum:request.event.currency }} |
+ {{ var.num_pending|togglesum:request.event.currency }} |
+ {{ var.num_paid|togglesum:request.event.currency }} |
+ {{ var.num_total|togglesum:request.event.currency }} |
{% endfor %}
{% endif %}
@@ -109,12 +109,12 @@
| {% trans "Total" %} |
- {{ total.num_canceled|togglesum }} |
- {{ total.num_refunded|togglesum }} |
- {{ total.num_expired|togglesum }} |
- {{ total.num_pending|togglesum }} |
- {{ total.num_paid|togglesum }} |
- {{ total.num_total|togglesum }} |
+ {{ total.num_canceled|togglesum:request.event.currency }} |
+ {{ total.num_refunded|togglesum:request.event.currency }} |
+ {{ total.num_expired|togglesum:request.event.currency }} |
+ {{ total.num_pending|togglesum:request.event.currency }} |
+ {{ total.num_paid|togglesum:request.event.currency }} |
+ {{ total.num_total|togglesum:request.event.currency }} |
diff --git a/src/pretix/control/templates/pretixcontrol/search/orders.html b/src/pretix/control/templates/pretixcontrol/search/orders.html
index 64db07e2dc..01eb551d85 100644
--- a/src/pretix/control/templates/pretixcontrol/search/orders.html
+++ b/src/pretix/control/templates/pretixcontrol/search/orders.html
@@ -2,6 +2,7 @@
{% load i18n %}
{% load eventurl %}
{% load urlreplace %}
+{% load money %}
{% load bootstrap3 %}
{% block title %}{% trans "Order search" %}{% endblock %}
{% block content %}
@@ -70,7 +71,7 @@
{% endif %}
{{ o.datetime|date:"SHORT_DATETIME_FORMAT" }} |
- {{ o.total|floatformat:2 }} {{ o.event.currency }} |
+ {{ o.total|money:o.event.currency }} |
{% include "pretixcontrol/orders/fragment_order_status.html" with order=o %} |
{% empty %}
diff --git a/src/pretix/control/templates/pretixcontrol/subevents/detail.html b/src/pretix/control/templates/pretixcontrol/subevents/detail.html
index db8d05a936..1499c6904d 100644
--- a/src/pretix/control/templates/pretixcontrol/subevents/detail.html
+++ b/src/pretix/control/templates/pretixcontrol/subevents/detail.html
@@ -120,7 +120,7 @@