forked from CGM_Public/pretix_original
* Refs #99 -- Fix stripe support for zero-decimal currencies * Add new money formatting method * Force decimal places in many places * Locale-aware currency rendering * Fix currencies in more places * More currency fixes
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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),
|
||||
''
|
||||
])
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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))
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()))
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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={
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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}
|
||||
|
||||
|
||||
55
src/pretix/base/templatetags/money.py
Normal file
55
src/pretix/base/templatetags/money.py
Normal file
@@ -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))
|
||||
@@ -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"),
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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', '') != '':
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
{% load i18n %}
|
||||
{% load bootstrap3 %}
|
||||
{% load eventurl %}
|
||||
{% load money %}
|
||||
{% load safelink %}
|
||||
{% load eventsignal %}
|
||||
{% block title %}
|
||||
@@ -257,7 +258,7 @@
|
||||
</div>
|
||||
<div class="col-md-3 col-xs-6 price">
|
||||
{% if event.settings.display_net_prices %}
|
||||
<strong>{{ event.currency }} {{ line.net_price|floatformat:2 }}</strong>
|
||||
<strong>{{ line.net_price|money:event.currency }}</strong>
|
||||
{% if line.tax_rate %}
|
||||
<br />
|
||||
<small>
|
||||
@@ -267,7 +268,7 @@
|
||||
</small>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<strong>{{ event.currency }} {{ line.price|floatformat:2 }}</strong>
|
||||
<strong>{{ line.price|money:event.currency }}</strong>
|
||||
{% if line.tax_rate and line.price %}
|
||||
<br />
|
||||
<small>
|
||||
@@ -291,7 +292,7 @@
|
||||
</div>
|
||||
<div class="col-md-3 col-xs-6 col-md-offset-5 price">
|
||||
{% if event.settings.display_net_prices %}
|
||||
<strong>{{ event.currency }} {{ fee.net_value|floatformat:2 }}</strong>
|
||||
<strong>{{ fee.net_value|money:event.currency }}</strong>
|
||||
{% if fee.tax_rate %}
|
||||
<br/>
|
||||
<small>
|
||||
@@ -301,7 +302,7 @@
|
||||
</small>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<strong>{{ event.currency }} {{ fee.value|floatformat:2 }}</strong>
|
||||
<strong>{{ fee.value|money:event.currency }}</strong>
|
||||
{% if fee.tax_rate %}
|
||||
<br/>
|
||||
<small>
|
||||
@@ -321,7 +322,7 @@
|
||||
<strong>{% trans "Net total" %}</strong>
|
||||
</div>
|
||||
<div class="col-md-3 col-xs-6 col-md-offset-5 price">
|
||||
{{ event.currency }} {{ items.net_total|floatformat:2 }}
|
||||
{{ items.net_total|money:event.currency }}
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
@@ -330,7 +331,7 @@
|
||||
<strong>{% trans "Taxes" %}</strong>
|
||||
</div>
|
||||
<div class="col-md-3 col-xs-6 col-md-offset-5 price">
|
||||
{{ event.currency }} {{ items.tax_total|floatformat:2 }}
|
||||
{{ items.tax_total|money:event.currency }}
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
@@ -340,7 +341,7 @@
|
||||
<strong>{% trans "Total" %}</strong>
|
||||
</div>
|
||||
<div class="col-md-3 col-xs-6 col-md-offset-5 price">
|
||||
<strong>{{ event.currency }} {{ items.total|floatformat:2 }}</strong>
|
||||
<strong>{{ items.total|money:event.currency }}</strong>
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
|
||||
@@ -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 %}
|
||||
</td>
|
||||
<td>{{ o.datetime|date:"SHORT_DATETIME_FORMAT" }}</td>
|
||||
<td class="text-right">{{ o.total|floatformat:2 }} {{ request.event.currency }}</td>
|
||||
<td class="text-right">{{ o.total|money:request.event.currency }}</td>
|
||||
<td class="text-right">{{ o.pcnt }}</td>
|
||||
<td class="text-right">{% include "pretixcontrol/orders/fragment_order_status.html" with order=o %}</td>
|
||||
</tr>
|
||||
|
||||
@@ -50,12 +50,12 @@
|
||||
{% if tup.0 %}
|
||||
<tr class="category">
|
||||
<th>{{ tup.0.name }}</th>
|
||||
<th>{{ tup.0.num_canceled|togglesum }}</th>
|
||||
<th>{{ tup.0.num_refunded|togglesum }}</th>
|
||||
<th>{{ tup.0.num_expired|togglesum }}</th>
|
||||
<th>{{ tup.0.num_pending|togglesum }}</th>
|
||||
<th>{{ tup.0.num_paid|togglesum }}</th>
|
||||
<th>{{ tup.0.num_total|togglesum }}</th>
|
||||
<th>{{ tup.0.num_canceled|togglesum:request.event.currency }}</th>
|
||||
<th>{{ tup.0.num_refunded|togglesum:request.event.currency }}</th>
|
||||
<th>{{ tup.0.num_expired|togglesum:request.event.currency }}</th>
|
||||
<th>{{ tup.0.num_pending|togglesum:request.event.currency }}</th>
|
||||
<th>{{ tup.0.num_paid|togglesum:request.event.currency }}</th>
|
||||
<th>{{ tup.0.num_total|togglesum:request.event.currency }}</th>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% for item in tup.1 %}
|
||||
@@ -63,43 +63,43 @@
|
||||
<td>{{ item.name }}</td>
|
||||
<td>
|
||||
<a href="{{ listurl }}?item={{ item.id }}&status=c&provider={{ item.provider }}">
|
||||
{{ item.num_canceled|togglesum }}
|
||||
{{ item.num_canceled|togglesum:request.event.currency }}
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<a href="{{ listurl }}?item={{ item.id }}&status=r&provider={{ item.provider }}">
|
||||
{{ item.num_refunded|togglesum }}
|
||||
{{ item.num_refunded|togglesum:request.event.currency }}
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<a href="{{ listurl }}?item={{ item.id }}&status=e&provider={{ item.provider }}">
|
||||
{{ item.num_expired|togglesum }}
|
||||
{{ item.num_expired|togglesum:request.event.currency }}
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<a href="{{ listurl }}?item={{ item.id }}&status=n&provider={{ item.provider }}">
|
||||
{{ item.num_pending|togglesum }}
|
||||
{{ item.num_pending|togglesum:request.event.currency }}
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<a href="{{ listurl }}?item={{ item.id }}&status=p&provider={{ item.provider }}">
|
||||
{{ item.num_paid|togglesum }}
|
||||
{{ item.num_paid|togglesum:request.event.currency }}
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
{{ item.num_total|togglesum }}
|
||||
{{ item.num_total|togglesum:request.event.currency }}
|
||||
</td>
|
||||
</tr>
|
||||
{% if item.has_variations %}
|
||||
{% for var in item.all_variations %}
|
||||
<tr class="variation {% if tup.0 %}categorized{% endif %}">
|
||||
<td>{{ var }}</td>
|
||||
<td>{{ var.num_canceled|togglesum }}</td>
|
||||
<td>{{ var.num_refunded|togglesum }}</td>
|
||||
<td>{{ var.num_expired|togglesum }}</td>
|
||||
<td>{{ var.num_pending|togglesum }}</td>
|
||||
<td>{{ var.num_paid|togglesum }}</td>
|
||||
<td>{{ var.num_total|togglesum }}</td>
|
||||
<td>{{ var.num_canceled|togglesum:request.event.currency }}</td>
|
||||
<td>{{ var.num_refunded|togglesum:request.event.currency }}</td>
|
||||
<td>{{ var.num_expired|togglesum:request.event.currency }}</td>
|
||||
<td>{{ var.num_pending|togglesum:request.event.currency }}</td>
|
||||
<td>{{ var.num_paid|togglesum:request.event.currency }}</td>
|
||||
<td>{{ var.num_total|togglesum:request.event.currency }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
@@ -109,12 +109,12 @@
|
||||
<tfoot>
|
||||
<tr class="total">
|
||||
<th>{% trans "Total" %}</th>
|
||||
<th>{{ total.num_canceled|togglesum }}</th>
|
||||
<th>{{ total.num_refunded|togglesum }}</th>
|
||||
<th>{{ total.num_expired|togglesum }}</th>
|
||||
<th>{{ total.num_pending|togglesum }}</th>
|
||||
<th>{{ total.num_paid|togglesum }}</th>
|
||||
<th>{{ total.num_total|togglesum }}</th>
|
||||
<th>{{ total.num_canceled|togglesum:request.event.currency }}</th>
|
||||
<th>{{ total.num_refunded|togglesum:request.event.currency }}</th>
|
||||
<th>{{ total.num_expired|togglesum:request.event.currency }}</th>
|
||||
<th>{{ total.num_pending|togglesum:request.event.currency }}</th>
|
||||
<th>{{ total.num_paid|togglesum:request.event.currency }}</th>
|
||||
<th>{{ total.num_total|togglesum:request.event.currency }}</th>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
|
||||
@@ -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 %}
|
||||
</td>
|
||||
<td>{{ o.datetime|date:"SHORT_DATETIME_FORMAT" }}</td>
|
||||
<td class="text-right">{{ o.total|floatformat:2 }} {{ o.event.currency }}</td>
|
||||
<td class="text-right">{{ o.total|money:o.event.currency }}</td>
|
||||
<td class="text-right">{% include "pretixcontrol/orders/fragment_order_status.html" with order=o %}</td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
|
||||
@@ -120,7 +120,7 @@
|
||||
<fieldset>
|
||||
<legend>{% trans "Item prices" %}</legend>
|
||||
{% for f in itemvar_forms %}
|
||||
{% bootstrap_field f.price layout="control" %}
|
||||
{% bootstrap_field f.price addon_after=request.event.currency layout="control" %}
|
||||
{% endfor %}
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{% extends "pretixcontrol/event/base.html" %}
|
||||
{% load i18n %}
|
||||
{% load eventurl %}
|
||||
{% load money %}
|
||||
{% load urlreplace %}
|
||||
{% block title %}{% trans "Waiting list" %}{% endblock %}
|
||||
{% block content %}
|
||||
@@ -65,9 +66,9 @@
|
||||
{% trans "Sales estimate" %}
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
{% blocktrans trimmed with amount=estimate|default:0|floatformat:2 currency=request.event.currency %}
|
||||
{% blocktrans trimmed with amount=estimate|default:0|money:request.event.currency %}
|
||||
If you can make enough room at your event to fit all the persons on the waiting list in, you
|
||||
could sell tickets worth an additional <strong>{{ amount }} {{ currency }}</strong>.
|
||||
could sell tickets worth an additional <strong>{{ amount }}</strong>.
|
||||
{% endblocktrans %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
from django import template
|
||||
from django.utils import formats
|
||||
from django.conf import settings
|
||||
from django.template.defaultfilters import floatformat
|
||||
from django.utils.html import conditional_escape
|
||||
from django.utils.safestring import mark_safe
|
||||
|
||||
@@ -7,7 +8,7 @@ register = template.Library()
|
||||
|
||||
|
||||
@register.filter(name='togglesum', needs_autoescape=True)
|
||||
def cut(value, autoescape=True):
|
||||
def togglesum_filter(value, arg='EUR', autoescape=True):
|
||||
def noop(x):
|
||||
return x
|
||||
|
||||
@@ -17,6 +18,10 @@ def cut(value, autoescape=True):
|
||||
esc = conditional_escape
|
||||
else:
|
||||
esc = noop
|
||||
|
||||
places = settings.CURRENCY_PLACES.get(arg, 2)
|
||||
return mark_safe('<span class="count">{0}</span><span class="sum-gross">{1}</span><span class="sum-net">{2}</span>'.format(
|
||||
esc(value[0]), esc(formats.localize(value[1])), esc(formats.localize(value[2]))
|
||||
esc(value[0]),
|
||||
esc(floatformat(value[1], places)),
|
||||
esc(floatformat(value[2], places))
|
||||
))
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import re
|
||||
from collections import OrderedDict
|
||||
from datetime import timedelta
|
||||
from decimal import Decimal
|
||||
from urllib.parse import urlsplit
|
||||
|
||||
from django.conf import settings
|
||||
@@ -26,6 +27,7 @@ from django.views.generic.detail import SingleObjectMixin
|
||||
from i18nfield.strings import LazyI18nString
|
||||
from pytz import timezone
|
||||
|
||||
from pretix.base.i18n import LazyCurrencyNumber
|
||||
from pretix.base.models import (
|
||||
CachedCombinedTicket, CachedTicket, Event, Item, ItemVariation, LogEntry,
|
||||
Order, RequiredAction, TaxRule, Voucher,
|
||||
@@ -34,6 +36,7 @@ from pretix.base.models.event import EventMetaValue
|
||||
from pretix.base.services import tickets
|
||||
from pretix.base.services.invoices import build_preview_invoice_pdf
|
||||
from pretix.base.signals import event_live_issues, register_ticket_outputs
|
||||
from pretix.base.templatetags.money import money_filter
|
||||
from pretix.control.forms.event import (
|
||||
CommentForm, DisplaySettingsForm, EventDeleteForm, EventMetaValueForm,
|
||||
EventSettingsForm, EventUpdateForm, InvoiceSettingsForm, MailSettingsForm,
|
||||
@@ -492,8 +495,8 @@ class MailSettingsPreview(EventPermissionRequiredMixin, View):
|
||||
return {
|
||||
'date': date_format(now() + timedelta(days=7), 'SHORT_DATE_FORMAT'),
|
||||
'expire_date': date_format(now() + timedelta(days=15), 'SHORT_DATE_FORMAT'),
|
||||
'payment_info': _('{} {} has been transferred to account <9999-9999-9999-9999> at {}').format(
|
||||
42.23, self.request.event.currency, date_format(now(), 'SHORT_DATETIME_FORMAT'))
|
||||
'payment_info': _('{} has been transferred to account <9999-9999-9999-9999> at {}').format(
|
||||
money_filter(Decimal('42.23'), self.request.event.currency), date_format(now(), 'SHORT_DATETIME_FORMAT'))
|
||||
}
|
||||
|
||||
# create index-language mapping
|
||||
@@ -508,7 +511,7 @@ class MailSettingsPreview(EventPermissionRequiredMixin, View):
|
||||
@cached_property
|
||||
def items(self):
|
||||
return {
|
||||
'mail_text_order_placed': ['total', 'currency', 'date', 'invoice_company',
|
||||
'mail_text_order_placed': ['total', 'currency', 'date', 'invoice_company', 'total_with_currency',
|
||||
'event', 'payment_info', 'url', 'invoice_name'],
|
||||
'mail_text_order_paid': ['event', 'url', 'invoice_name', 'invoice_company', 'payment_info'],
|
||||
'mail_text_order_free': ['event', 'url', 'invoice_name', 'invoice_company'],
|
||||
@@ -536,6 +539,7 @@ class MailSettingsPreview(EventPermissionRequiredMixin, View):
|
||||
return {
|
||||
'event': self.request.event.name,
|
||||
'total': 42.23,
|
||||
'total_with_currency': LazyCurrencyNumber(42.23, self.request.event.currency),
|
||||
'currency': self.request.event.currency,
|
||||
'url': self.generate_order_url(user_orders[0]['code'], user_orders[0]['secret']),
|
||||
'orders': '\n'.join(orders),
|
||||
|
||||
34
src/pretix/helpers/money.py
Normal file
34
src/pretix/helpers/money.py
Normal file
@@ -0,0 +1,34 @@
|
||||
from decimal import Decimal
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.validators import DecimalValidator
|
||||
from django.forms import NumberInput, TextInput
|
||||
from django.utils import formats
|
||||
|
||||
|
||||
class DecimalTextInput(TextInput):
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.places = kwargs.pop('places', 2)
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def format_value(self, value):
|
||||
"""
|
||||
Return a value as it should appear when rendered in a template.
|
||||
"""
|
||||
if value == '' or value is None:
|
||||
return None
|
||||
if isinstance(value, str):
|
||||
return value
|
||||
return formats.localize_input(value.quantize(Decimal('1') / 10 ** self.places))
|
||||
|
||||
|
||||
def change_decimal_field(field, currency):
|
||||
places = settings.CURRENCY_PLACES.get(currency, 2)
|
||||
field.decimal_places = places
|
||||
if isinstance(field.widget, NumberInput):
|
||||
field.widget.attrs['step'] = str(Decimal('1') / 10 ** places).lower()
|
||||
elif isinstance(field.widget, TextInput):
|
||||
field.widget = DecimalTextInput(places=places)
|
||||
v = [v for v in field.validators if isinstance(v, DecimalValidator)]
|
||||
if len(v) == 1:
|
||||
v[0].decimal_places = places
|
||||
@@ -1,7 +1,7 @@
|
||||
{% load i18n %}{% load l10n %}{% blocktrans with bank=details|safe code=order.full_code total=order.total|localize currency=event.currency %}
|
||||
{% load i18n %}{% load l10n %}{% load money %}{% blocktrans with bank=details|safe code=order.full_code total=order.total|money:event.currency %}
|
||||
Please transfer the full amount to the following bank account.
|
||||
|
||||
Reference: {{ code }}
|
||||
Amount: {{ total }} {{ currency }}
|
||||
Amount: {{ total }}
|
||||
{{ bank }}
|
||||
{% endblocktrans %}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{% load i18n %}
|
||||
{% load l10n %}
|
||||
{% load money %}
|
||||
|
||||
<p>{% blocktrans trimmed %}
|
||||
Please transfer the full amount to the following bank account:
|
||||
@@ -7,6 +8,6 @@
|
||||
|
||||
<address>
|
||||
{{ details|linebreaksbr }}<br />
|
||||
{% trans "Amount:" %} {{ order.total|localize }} {{ event.currency }}<br />
|
||||
{% trans "Amount:" %} {{ order.total|money:event.currency }}<br />
|
||||
<strong>{% trans "Reference code (important):" %} {{ order.full_code }}</strong>
|
||||
</address>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{% load i18n %}
|
||||
{% load rich_text %}
|
||||
{% load money %}
|
||||
{% load staticfiles %}
|
||||
<div class="table-responsive">
|
||||
{% csrf_token %}
|
||||
@@ -85,7 +86,7 @@
|
||||
<td>
|
||||
{% if trans.order %}
|
||||
<a href="{% url "control:event.order" event=trans.order.event.slug organizer=request.organizer.slug code=trans.order.code %}"
|
||||
data-toggle="tooltip" title="{{ trans.order.total|floatformat:2 }} {{ trans.order.event.currency }}">
|
||||
data-toggle="tooltip" title="{{ trans.order.total|money:trans.order.event.currency }}">
|
||||
{% if not request.event %}
|
||||
{{ trans.order.event.slug|upper }}-{{ trans.order.code }}
|
||||
{% else %}
|
||||
|
||||
@@ -18,6 +18,7 @@ from pretix.base.models import Order, Quota
|
||||
from pretix.base.services.mail import SendMailException
|
||||
from pretix.base.services.orders import mark_order_paid
|
||||
from pretix.base.settings import SettingsSandbox
|
||||
from pretix.base.templatetags.money import money_filter
|
||||
from pretix.control.permissions import (
|
||||
EventPermissionRequiredMixin, OrganizerPermissionRequiredMixin,
|
||||
)
|
||||
@@ -147,8 +148,6 @@ class ActionView(View):
|
||||
})
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
from django.utils.formats import localize
|
||||
|
||||
u = request.GET.get('query', '')
|
||||
if len(u) < 2:
|
||||
return JsonResponse({'results': []})
|
||||
@@ -178,7 +177,7 @@ class ActionView(View):
|
||||
{
|
||||
'code': o.event.slug.upper() + '-' + o.code,
|
||||
'status': o.get_status_display(),
|
||||
'total': localize(o.total) + ' ' + o.event.currency
|
||||
'total': money_filter(o.total, o.event.currency)
|
||||
} for o in qs
|
||||
]
|
||||
})
|
||||
|
||||
@@ -6,7 +6,7 @@ from defusedcsv import csv
|
||||
from django import forms
|
||||
from django.db.models import Max, OuterRef, Subquery
|
||||
from django.db.models.functions import Coalesce
|
||||
from django.utils.formats import date_format, localize
|
||||
from django.utils.formats import date_format
|
||||
from django.utils.timezone import is_aware, make_aware
|
||||
from django.utils.translation import pgettext, ugettext as _, ugettext_lazy
|
||||
from pytz import UTC
|
||||
@@ -15,6 +15,7 @@ from reportlab.platypus import Flowable, Paragraph, Spacer, Table, TableStyle
|
||||
|
||||
from pretix.base.exporter import BaseExporter
|
||||
from pretix.base.models import Checkin, Order, OrderPosition, Question
|
||||
from pretix.base.templatetags.money import money_filter
|
||||
from pretix.plugins.reports.exporters import ReportlabExportMixin
|
||||
|
||||
|
||||
@@ -200,7 +201,7 @@ class PDFCheckinList(ReportlabExportMixin, BaseCheckinList):
|
||||
op.order.code,
|
||||
name,
|
||||
str(op.item.name) + (" – " + str(op.variation.value) if op.variation else "") + "\n" +
|
||||
self.event.currency + " " + localize(op.price),
|
||||
money_filter(op.price, self.event.currency),
|
||||
]
|
||||
acache = {}
|
||||
for a in op.answers.all():
|
||||
|
||||
@@ -55,6 +55,8 @@ class Paypal(BasePaymentProvider):
|
||||
('client_id',
|
||||
forms.CharField(
|
||||
label=_('Client ID'),
|
||||
max_length=80,
|
||||
min_length=80,
|
||||
help_text=_('<a target="_blank" rel="noopener" href="{docs_url}">{text}</a>').format(
|
||||
text=_('Click here for a tutorial on how to obtain the required keys'),
|
||||
docs_url='https://docs.pretix.eu/en/latest/user/payments/paypal.html'
|
||||
@@ -63,6 +65,8 @@ class Paypal(BasePaymentProvider):
|
||||
('secret',
|
||||
forms.CharField(
|
||||
label=_('Secret'),
|
||||
max_length=80,
|
||||
min_length=80,
|
||||
))
|
||||
]
|
||||
)
|
||||
|
||||
@@ -7,7 +7,8 @@ from django import forms
|
||||
from django.conf import settings
|
||||
from django.contrib.staticfiles import finders
|
||||
from django.db.models import Sum
|
||||
from django.utils.formats import date_format, localize
|
||||
from django.template.defaultfilters import floatformat
|
||||
from django.utils.formats import date_format
|
||||
from django.utils.timezone import get_current_timezone, now
|
||||
from django.utils.translation import pgettext, pgettext_lazy, ugettext as _
|
||||
|
||||
@@ -194,49 +195,50 @@ class OverviewReport(Report):
|
||||
]
|
||||
|
||||
items_by_category, total = order_overview(self.event, subevent=self.form_data.get('subevent'))
|
||||
places = settings.CURRENCY_PLACES.get(self.event.currency, 2)
|
||||
|
||||
for tup in items_by_category:
|
||||
if tup[0]:
|
||||
tstyledata.append(('FONTNAME', (0, len(tdata)), (-1, len(tdata)), 'OpenSansBd'))
|
||||
tdata.append([
|
||||
tup[0].name,
|
||||
str(tup[0].num_canceled[0]), localize(tup[0].num_canceled[1]),
|
||||
str(tup[0].num_refunded[0]), localize(tup[0].num_refunded[1]),
|
||||
str(tup[0].num_expired[0]), localize(tup[0].num_expired[1]),
|
||||
str(tup[0].num_pending[0]), localize(tup[0].num_pending[1]),
|
||||
str(tup[0].num_paid[0]), localize(tup[0].num_paid[1]),
|
||||
str(tup[0].num_total[0]), localize(tup[0].num_total[1]),
|
||||
str(tup[0].num_canceled[0]), floatformat(tup[0].num_canceled[1], places),
|
||||
str(tup[0].num_refunded[0]), floatformat(tup[0].num_refunded[1], places),
|
||||
str(tup[0].num_expired[0]), floatformat(tup[0].num_expired[1], places),
|
||||
str(tup[0].num_pending[0]), floatformat(tup[0].num_pending[1], places),
|
||||
str(tup[0].num_paid[0]), floatformat(tup[0].num_paid[1], places),
|
||||
str(tup[0].num_total[0]), floatformat(tup[0].num_total[1], places),
|
||||
])
|
||||
for item in tup[1]:
|
||||
tdata.append([
|
||||
" " + str(item.name),
|
||||
str(item.num_canceled[0]), localize(item.num_canceled[1]),
|
||||
str(item.num_refunded[0]), localize(item.num_refunded[1]),
|
||||
str(item.num_expired[0]), localize(item.num_expired[1]),
|
||||
str(item.num_pending[0]), localize(item.num_pending[1]),
|
||||
str(item.num_paid[0]), localize(item.num_paid[1]),
|
||||
str(item.num_total[0]), localize(item.num_total[1]),
|
||||
str(item.num_canceled[0]), floatformat(item.num_canceled[1], places),
|
||||
str(item.num_refunded[0]), floatformat(item.num_refunded[1], places),
|
||||
str(item.num_expired[0]), floatformat(item.num_expired[1], places),
|
||||
str(item.num_pending[0]), floatformat(item.num_pending[1], places),
|
||||
str(item.num_paid[0]), floatformat(item.num_paid[1], places),
|
||||
str(item.num_total[0]), floatformat(item.num_total[1], places),
|
||||
])
|
||||
if item.has_variations:
|
||||
for var in item.all_variations:
|
||||
tdata.append([
|
||||
" " + str(var),
|
||||
str(var.num_canceled[0]), localize(var.num_canceled[1]),
|
||||
str(var.num_refunded[0]), localize(var.num_refunded[1]),
|
||||
str(var.num_expired[0]), localize(var.num_expired[1]),
|
||||
str(var.num_pending[0]), localize(var.num_pending[1]),
|
||||
str(var.num_paid[0]), localize(var.num_paid[1]),
|
||||
str(var.num_total[0]), localize(var.num_total[1]),
|
||||
str(var.num_canceled[0]), floatformat(var.num_canceled[1], places),
|
||||
str(var.num_refunded[0]), floatformat(var.num_refunded[1], places),
|
||||
str(var.num_expired[0]), floatformat(var.num_expired[1], places),
|
||||
str(var.num_pending[0]), floatformat(var.num_pending[1], places),
|
||||
str(var.num_paid[0]), floatformat(var.num_paid[1], places),
|
||||
str(var.num_total[0]), floatformat(var.num_total[1], places),
|
||||
])
|
||||
|
||||
tdata.append([
|
||||
_("Total"),
|
||||
str(total['num_canceled'][0]), localize(total['num_canceled'][1]),
|
||||
str(total['num_refunded'][0]), localize(total['num_refunded'][1]),
|
||||
str(total['num_expired'][0]), localize(total['num_expired'][1]),
|
||||
str(total['num_pending'][0]), localize(total['num_pending'][1]),
|
||||
str(total['num_paid'][0]), localize(total['num_paid'][1]),
|
||||
str(total['num_total'][0]), localize(total['num_total'][1]),
|
||||
str(total['num_canceled'][0]), floatformat(total['num_canceled'][1], places),
|
||||
str(total['num_refunded'][0]), floatformat(total['num_refunded'][1], places),
|
||||
str(total['num_expired'][0]), floatformat(total['num_expired'][1], places),
|
||||
str(total['num_pending'][0]), floatformat(total['num_pending'][1], places),
|
||||
str(total['num_paid'][0]), floatformat(total['num_paid'][1], places),
|
||||
str(total['num_total'][0]), floatformat(total['num_total'][1], places),
|
||||
])
|
||||
|
||||
table = Table(tdata, colWidths=colwidths, repeatRows=3)
|
||||
|
||||
@@ -5,6 +5,7 @@ from collections import OrderedDict
|
||||
|
||||
import stripe
|
||||
from django import forms
|
||||
from django.conf import settings
|
||||
from django.contrib import messages
|
||||
from django.template.loader import get_template
|
||||
from django.utils.translation import ugettext, ugettext_lazy as _
|
||||
@@ -165,6 +166,10 @@ class StripeMethod(BasePaymentProvider):
|
||||
def order_prepare(self, request, order):
|
||||
return self.checkout_prepare(request, None)
|
||||
|
||||
def _get_amount(self, order):
|
||||
places = settings.CURRENCY_PLACES.get(self.event.currency, 2)
|
||||
return int(order.total * 10 ** places)
|
||||
|
||||
def _init_api(self):
|
||||
stripe.api_version = '2017-06-05'
|
||||
stripe.api_key = self.settings.get('secret_key')
|
||||
@@ -180,7 +185,7 @@ class StripeMethod(BasePaymentProvider):
|
||||
def _charge_source(self, request, source, order):
|
||||
try:
|
||||
charge = stripe.Charge.create(
|
||||
amount=int(order.total * 100),
|
||||
amount=self._get_amount(order),
|
||||
currency=self.event.currency.lower(),
|
||||
source=source,
|
||||
metadata={
|
||||
@@ -269,7 +274,7 @@ class StripeMethod(BasePaymentProvider):
|
||||
if order.payment_info:
|
||||
payment_info = json.loads(order.payment_info)
|
||||
if 'amount' in payment_info:
|
||||
payment_info['amount'] /= 100
|
||||
payment_info['amount'] /= 10 ** settings.CURRENCY_PLACES.get(self.event.currency, 2)
|
||||
else:
|
||||
payment_info = None
|
||||
template = get_template('pretixplugins/stripe/control.html')
|
||||
@@ -411,7 +416,7 @@ class StripeCC(StripeMethod):
|
||||
request.session['payment_stripe_order_secret'] = order.secret
|
||||
source = stripe.Source.create(
|
||||
type='three_d_secure',
|
||||
amount=int(order.total * 100),
|
||||
amount=self._get_amount(order),
|
||||
currency=self.event.currency.lower(),
|
||||
three_d_secure={
|
||||
'card': src.id
|
||||
@@ -479,7 +484,7 @@ class StripeGiropay(StripeMethod):
|
||||
try:
|
||||
source = stripe.Source.create(
|
||||
type='giropay',
|
||||
amount=int(order.total * 100),
|
||||
amount=self._get_amount(order),
|
||||
currency=self.event.currency.lower(),
|
||||
metadata={
|
||||
'order': str(order.id),
|
||||
@@ -538,7 +543,7 @@ class StripeIdeal(StripeMethod):
|
||||
def _create_source(self, request, order):
|
||||
source = stripe.Source.create(
|
||||
type='ideal',
|
||||
amount=int(order.total * 100),
|
||||
amount=self._get_amount(order),
|
||||
currency=self.event.currency.lower(),
|
||||
metadata={
|
||||
'order': str(order.id),
|
||||
@@ -585,7 +590,7 @@ class StripeAlipay(StripeMethod):
|
||||
def _create_source(self, request, order):
|
||||
source = stripe.Source.create(
|
||||
type='alipay',
|
||||
amount=int(order.total * 100),
|
||||
amount=self._get_amount(order),
|
||||
currency=self.event.currency.lower(),
|
||||
metadata={
|
||||
'order': str(order.id),
|
||||
@@ -634,7 +639,7 @@ class StripeBancontact(StripeMethod):
|
||||
try:
|
||||
source = stripe.Source.create(
|
||||
type='bancontact',
|
||||
amount=int(order.total * 100),
|
||||
amount=self._get_amount(order),
|
||||
currency=self.event.currency.lower(),
|
||||
metadata={
|
||||
'order': str(order.id),
|
||||
@@ -706,7 +711,7 @@ class StripeSofort(StripeMethod):
|
||||
def _create_source(self, request, order):
|
||||
source = stripe.Source.create(
|
||||
type='sofort',
|
||||
amount=int(order.total * 100),
|
||||
amount=self._get_amount(order),
|
||||
currency=self.event.currency.lower(),
|
||||
metadata={
|
||||
'order': str(order.id),
|
||||
|
||||
@@ -11,7 +11,7 @@ from django.core.files import File
|
||||
from django.core.files.storage import default_storage
|
||||
from django.http import HttpRequest
|
||||
from django.template.loader import get_template
|
||||
from django.utils.formats import date_format, localize
|
||||
from django.utils.formats import date_format
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from pytz import timezone
|
||||
from reportlab.graphics import renderPDF
|
||||
@@ -29,6 +29,7 @@ from reportlab.platypus import Paragraph
|
||||
|
||||
from pretix.base.i18n import language
|
||||
from pretix.base.models import Order, OrderPosition
|
||||
from pretix.base.templatetags.money import money_filter
|
||||
from pretix.base.ticketoutput import BaseTicketOutput
|
||||
from pretix.plugins.ticketoutputpdf.signals import (
|
||||
get_fonts, layout_text_variables,
|
||||
@@ -81,7 +82,7 @@ DEFAULT_VARIABLES = OrderedDict((
|
||||
("price", {
|
||||
"label": _("Price"),
|
||||
"editor_sample": _("123.45 EUR"),
|
||||
"evaluate": lambda op, order, event: '{} {}'.format(event.currency, localize(op.price))
|
||||
"evaluate": lambda op, order, event: money_filter(op.price, event.currency)
|
||||
}),
|
||||
("attendee_name", {
|
||||
"label": _("Attendee name"),
|
||||
|
||||
@@ -13,6 +13,7 @@ from pretix.base.forms.questions import (
|
||||
)
|
||||
from pretix.base.models import ItemVariation
|
||||
from pretix.base.models.tax import TAXED_ZERO
|
||||
from pretix.base.templatetags.money import money_filter
|
||||
from pretix.base.templatetags.rich_text import rich_text
|
||||
from pretix.base.validators import EmailBlacklistValidator
|
||||
from pretix.presale.signals import contact_form_fields
|
||||
@@ -133,17 +134,17 @@ class AddOnsForm(forms.Form):
|
||||
name=label
|
||||
)
|
||||
elif not price.rate:
|
||||
n = _('{name} (+ {currency} {price})').format(
|
||||
name=label, currency=event.currency, price=number_format(price.gross)
|
||||
n = _('{name} (+ {price})').format(
|
||||
name=label, price=money_filter(price.gross, event.currency)
|
||||
)
|
||||
elif event.settings.display_net_prices:
|
||||
n = _('{name} (+ {currency} {price} plus {taxes}% {taxname})').format(
|
||||
name=label, currency=event.currency, price=number_format(price.net),
|
||||
n = _('{name} (+ {price} plus {taxes}% {taxname})').format(
|
||||
name=label, price=money_filter(price.net, event.currency),
|
||||
taxes=number_format(price.rate), taxname=price.name
|
||||
)
|
||||
else:
|
||||
n = _('{name} (+ {currency} {price} incl. {taxes}% {taxname})').format(
|
||||
name=label, currency=event.currency, price=number_format(price.gross),
|
||||
n = _('{name} (+ {price} incl. {taxes}% {taxname})').format(
|
||||
name=label, price=money_filter(price.gross, event.currency),
|
||||
taxes=number_format(price.rate), taxname=price.name
|
||||
)
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{% extends "pretixpresale/event/checkout_base.html" %}
|
||||
{% load i18n %}
|
||||
{% load money %}
|
||||
{% load bootstrap3 %}
|
||||
{% block inner %}
|
||||
<p>{% trans "Please select how you want to pay." %}</p>
|
||||
@@ -12,7 +13,7 @@
|
||||
<div class="panel-heading">
|
||||
<h4 class="panel-title">
|
||||
{% if show_fees %}
|
||||
<strong class="pull-right">{% if p.fee < 0 %}-{% else %}+{% endif %} {{ p.fee|floatformat:2|cut:"-" }} {{ event.currency }}</strong>
|
||||
<strong class="pull-right">{% if p.fee < 0 %}-{% else %}+{% endif %} {{ p.fee|money:event.currency|cut:"-" }}</strong>
|
||||
{% endif %}
|
||||
<input type="radio" name="payment" value="{{ p.provider.identifier }}"
|
||||
data-parent="#payment_accordion"
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{% load i18n %}
|
||||
{% load eventurl %}
|
||||
{% load safelink %}
|
||||
{% load money %}
|
||||
{% blocktrans asvar s_taxes %}taxes{% endblocktrans %}
|
||||
{% for line in cart.positions %}
|
||||
<div class="row cart-row {% if download and line.item.admission|default:event.settings.ticket_download_nonadm %}has-downloads{% endif %}">
|
||||
@@ -71,9 +72,9 @@
|
||||
<div class="count"> </div>
|
||||
<div class="singleprice price">
|
||||
{% if event.settings.display_net_prices %}
|
||||
{{ event.currency }} {{ line.net_price|floatformat:2 }}
|
||||
{{ line.net_price|money:event.currency }}
|
||||
{% else %}
|
||||
{{ event.currency }} {{ line.price|floatformat:2 }}
|
||||
{{ line.price|money:event.currency }}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% else %}
|
||||
@@ -113,15 +114,15 @@
|
||||
</div>
|
||||
<div class="singleprice price">
|
||||
{% if event.settings.display_net_prices %}
|
||||
{{ event.currency }} {{ line.net_price|floatformat:2 }}
|
||||
{{ line.net_price|money:event.currency }}
|
||||
{% else %}
|
||||
{{ event.currency }} {{ line.price|floatformat:2 }}
|
||||
{{ line.price|money:event.currency }}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="totalprice price">
|
||||
{% if event.settings.display_net_prices %}
|
||||
<strong>{{ event.currency }} {{ line.net_total|floatformat:2 }}</strong>
|
||||
<strong>{{ line.net_total|money:event.currency }}</strong>
|
||||
{% if line.tax_rate and line.total %}
|
||||
<br />
|
||||
<small>
|
||||
@@ -131,7 +132,7 @@
|
||||
</small>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<strong>{{ event.currency }} {{ line.total|floatformat:2 }}</strong>
|
||||
<strong>{{ line.total|money:event.currency }}</strong>
|
||||
{% if line.tax_rate and line.total %}
|
||||
<br />
|
||||
<small>
|
||||
@@ -165,7 +166,7 @@
|
||||
</div>
|
||||
<div class="col-md-3 col-xs-6 col-md-offset-5 price">
|
||||
{% if event.settings.display_net_prices %}
|
||||
<strong>{{ event.currency }} {{ fee.net_value|floatformat:2 }}</strong>
|
||||
<strong>{{ fee.net_value|money:event.currency }}</strong>
|
||||
{% if fee.tax_rate %}
|
||||
<br />
|
||||
<small>
|
||||
@@ -175,7 +176,7 @@
|
||||
</small>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<strong>{{ event.currency }} {{ fee.value|floatformat:2 }}</strong>
|
||||
<strong>{{ fee.value|money:event.currency }}</strong>
|
||||
{% if fee.tax_rate %}
|
||||
<br />
|
||||
<small>
|
||||
@@ -195,7 +196,7 @@
|
||||
<strong>{% trans "Net total" %}</strong>
|
||||
</div>
|
||||
<div class="col-md-3 col-xs-6 col-md-offset-5 price">
|
||||
{{ event.currency }} {{ cart.net_total|floatformat:2 }}
|
||||
{{ cart.net_total|money:event.currency }}
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
@@ -204,7 +205,7 @@
|
||||
<strong>{% trans "Taxes" %}</strong>
|
||||
</div>
|
||||
<div class="col-md-3 col-xs-6 col-md-offset-5 price">
|
||||
{{ event.currency }} {{ cart.tax_total|floatformat:2 }}
|
||||
{{ cart.tax_total|money:event.currency }}
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
@@ -214,7 +215,7 @@
|
||||
<strong>{% trans "Total" %}</strong>
|
||||
</div>
|
||||
<div class="col-md-3 col-xs-6 col-md-offset-5 price">
|
||||
<strong>{{ event.currency }} {{ cart.total|floatformat:2 }}</strong>
|
||||
<strong>{{ cart.total|money:event.currency }}</strong>
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
{% load i18n %}
|
||||
{% load l10n %}
|
||||
{% load eventurl %}
|
||||
{% load money %}
|
||||
{% load thumbnail %}
|
||||
{% load eventsignal %}
|
||||
{% load rich_text %}
|
||||
@@ -250,13 +251,13 @@
|
||||
</div>
|
||||
<div class="col-md-2 col-xs-6 price">
|
||||
{% if item.min_price != item.max_price or item.free_price %}
|
||||
{% blocktrans trimmed with minprice=item.min_price|floatformat:2 currency=event.currency %}
|
||||
from {{ currency }} {{ minprice }}
|
||||
{% blocktrans trimmed with minprice=item.min_price|money:event.currency %}
|
||||
from {{ minprice }}
|
||||
{% endblocktrans %}
|
||||
{% elif not item.min_price and not item.max_price %}
|
||||
{% trans "FREE" context "price" %}
|
||||
{% else %}
|
||||
{{ event.currency }} {{ item.min_price|floatformat:2 }}
|
||||
{{ item.min_price|money:event.currency }}
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="col-md-2 col-xs-6 availability-box">
|
||||
@@ -288,18 +289,18 @@
|
||||
<span class="input-group-addon">{{ event.currency }}</span>
|
||||
<input type="number" class="form-control input-item-price"
|
||||
placeholder="0"
|
||||
min="{% if event.settings.display_net_prices %}{{ var.display_price.net|stringformat:"0.2f" }}{% else %}{{ var.display_price.gross|stringformat:"0.2f" }}{% endif %}"
|
||||
min="{% if event.settings.display_net_prices %}{{ var.display_price.net|money_numberfield:event.currency }}{% else %}{{ var.display_price.gross|money_numberfield:event.currency }}{% endif %}"
|
||||
name="price_{{ item.id }}_{{ var.id }}"
|
||||
step="any"
|
||||
value="{% if event.settings.display_net_prices %}{{var.display_price.net|stringformat:"0.2f" }}{% else %}{{ var.display_price.gross|stringformat:"0.2f" }}{% endif %}"
|
||||
value="{% if event.settings.display_net_prices %}{{var.display_price.net|money_numberfield:event.currency }}{% else %}{{ var.display_price.gross|money_numberfield:event.currency }}{% endif %}"
|
||||
>
|
||||
</div>
|
||||
{% elif not var.display_price.gross %}
|
||||
{% trans "FREE" context "price" %}
|
||||
{% elif event.settings.display_net_prices %}
|
||||
{{ event.currency }} {{ var.display_price.net|floatformat:2 }}
|
||||
{{ var.display_price.net|money:event.currency }}
|
||||
{% else %}
|
||||
{{ event.currency }} {{ var.display_price.gross|floatformat:2 }}
|
||||
{{ var.display_price.gross|money:event.currency }}
|
||||
{% endif %}
|
||||
{% if var.display_price.rate and var.display_price.gross and event.settings.display_net_prices %}
|
||||
<small>{% blocktrans trimmed with rate=var.display_price.rate name=var.display_price.name %}
|
||||
@@ -377,17 +378,17 @@
|
||||
<div class="input-group input-group-price">
|
||||
<span class="input-group-addon">{{ event.currency }}</span>
|
||||
<input type="number" class="form-control input-item-price" placeholder="0"
|
||||
min="{% if event.settings.display_net_prices %}{{ item.display_price.net|stringformat:"0.2f" }}{% else %}{{ item.display_price.gross|stringformat:"0.2f" }}{% endif %}"
|
||||
min="{% if event.settings.display_net_prices %}{{ item.display_price.net|money_numberfield:event.currency }}{% else %}{{ item.display_price.gross|money_numberfield:event.currency }}{% endif %}"
|
||||
name="price_{{ item.id }}"
|
||||
value="{% if event.settings.display_net_prices %}{{item.display_price.net|stringformat:"0.2f" }}{% else %}{{ item.display_price.gross|stringformat:"0.2f" }}{% endif %}"
|
||||
value="{% if event.settings.display_net_prices %}{{item.display_price.net|money_numberfield:event.currency }}{% else %}{{ item.display_price.gross|money_numberfield:event.currency }}{% endif %}"
|
||||
step="any">
|
||||
</div>
|
||||
{% elif not item.display_price.gross %}
|
||||
{% trans "FREE" context "price" %}
|
||||
{% elif event.settings.display_net_prices %}
|
||||
{{ event.currency }} {{ item.display_price.net|floatformat:2 }}
|
||||
{{ item.display_price.net|money:event.currency }}
|
||||
{% else %}
|
||||
{{ event.currency }} {{ item.display_price.gross|floatformat:2 }}
|
||||
{{ item.display_price.gross|money:event.currency }}
|
||||
{% endif %}
|
||||
{% if item.display_price.rate and item.display_price.gross and event.settings.display_net_prices %}
|
||||
<small>{% blocktrans trimmed with rate=item.display_price.rate name=item.display_price.name %}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{% extends "pretixpresale/event/base.html" %}
|
||||
{% load i18n %}
|
||||
{% load eventurl %}
|
||||
{% load money %}
|
||||
{% block title %}{% trans "Change payment method" %}{% endblock %}
|
||||
{% block content %}
|
||||
<h2>
|
||||
@@ -22,7 +23,7 @@
|
||||
<div class="panel-heading">
|
||||
<h4 class="panel-title">
|
||||
<label class="radio">
|
||||
<strong class="pull-right">{% if p.fee_diff >= 0 %}+{% else %}-{% endif %} {{ p.fee_diff_abs|floatformat:2 }} {{ event.currency }}</strong>
|
||||
<strong class="pull-right">{% if p.fee_diff >= 0 %}+{% else %}-{% endif %} {{ p.fee_diff_abs|money:event.currency }}</strong>
|
||||
<input type="radio" name="payment" value="{{ p.provider.identifier }}"
|
||||
data-parent="#payment_accordion"
|
||||
{% if selected == p.provider.identifier %}checked="checked"{% endif %}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{% extends "pretixpresale/event/base.html" %}
|
||||
{% load i18n %}
|
||||
{% load eventurl %}
|
||||
{% load money %}
|
||||
{% block title %}{% trans "Pay order" %}{% endblock %}
|
||||
{% block content %}
|
||||
<h2>
|
||||
@@ -18,8 +19,8 @@
|
||||
<div class="panel-heading">
|
||||
<div class="pull-right">
|
||||
<strong>
|
||||
{% blocktrans trimmed with total=order.total|floatformat:2 currency=request.event.currency %}
|
||||
Total: {{ total }} {{ currency }}
|
||||
{% blocktrans trimmed with total=order.total|money:request.event.currency %}
|
||||
Total: {{ total }}
|
||||
{% endblocktrans %}
|
||||
</strong>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{% extends "pretixpresale/event/base.html" %}
|
||||
{% load i18n %}
|
||||
{% load l10n %}
|
||||
{% load money %}
|
||||
{% load eventurl %}
|
||||
{% load eventsignal %}
|
||||
{% load thumbnail %}
|
||||
@@ -56,13 +57,13 @@
|
||||
</div>
|
||||
<div class="col-md-2 col-xs-6 price">
|
||||
{% if item.min_price != item.max_price or item.free_price %}
|
||||
{% blocktrans trimmed with minprice=item.min_price|floatformat:2 currency=event.currency %}
|
||||
from {{ currency }} {{ minprice }}
|
||||
{% blocktrans trimmed with minprice=item.min_price|money:event.currency %}
|
||||
from {{ minprice }}
|
||||
{% endblocktrans %}
|
||||
{% elif not item.min_price and not item.max_price %}
|
||||
{% trans "FREE" context "price" %}
|
||||
{% else %}
|
||||
{{ event.currency }} {{ item.min_price|floatformat:2 }}
|
||||
{{ item.min_price|money:event.currency }}
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="col-md-2 col-xs-6 availability-box">
|
||||
@@ -94,9 +95,9 @@
|
||||
{% elif not var.display_price.gross %}
|
||||
{% trans "FREE" context "price" %}
|
||||
{% elif event.settings.display_net_prices %}
|
||||
{{ event.currency }} {{ var.display_price.net|floatformat:2 }}
|
||||
{{ var.display_price.net|money:event.currency }}
|
||||
{% else %}
|
||||
{{ event.currency }} {{ var.display_price.gross|floatformat:2 }}
|
||||
{{ var.display_price.gross|money:event.currency }}
|
||||
{% endif %}
|
||||
{% if var.display_price.rate and var.display_price.gross and event.settings.display_net_prices %}
|
||||
<small>{% blocktrans trimmed with rate=var.display_price.rate name=var.display_price.name %}
|
||||
@@ -164,9 +165,9 @@
|
||||
{% elif not item.display_price.gross %}
|
||||
{% trans "FREE" context "price" %}
|
||||
{% elif event.settings.display_net_prices %}
|
||||
{{ event.currency }} {{ item.display_price.net|floatformat:2 }}
|
||||
{{ item.display_price.net|money:event.currency }}
|
||||
{% else %}
|
||||
{{ event.currency }} {{ item.display_price.gross|floatformat:2 }}
|
||||
{{ item.display_price.gross|money:event.currency }}
|
||||
{% endif %}
|
||||
{% if item.display_price.rate and item.display_price.gross and event.settings.display_net_prices %}
|
||||
<small>{% blocktrans trimmed with rate=item.display_price.rate name=item.display_price.name %}
|
||||
|
||||
@@ -103,6 +103,24 @@ FETCH_ECB_RATES = config.getboolean('pretix', 'ecb_rates', fallback=True)
|
||||
|
||||
DEFAULT_CURRENCY = config.get('pretix', 'currency', fallback='EUR')
|
||||
CURRENCIES = list(currencies)
|
||||
CURRENCY_PLACES = {
|
||||
# default is 2
|
||||
'BIF': 0,
|
||||
'CLP': 0,
|
||||
'DJF': 0,
|
||||
'GNF': 0,
|
||||
'JPY': 0,
|
||||
'KMF': 0,
|
||||
'KRW': 0,
|
||||
'MGA': 0,
|
||||
'PYG': 0,
|
||||
'RWF': 0,
|
||||
'VND': 0,
|
||||
'VUV': 0,
|
||||
'XAF': 0,
|
||||
'XOF': 0,
|
||||
'XPF': 0,
|
||||
}
|
||||
|
||||
ALLOWED_HOSTS = ['*']
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@ django-markup
|
||||
markdown
|
||||
bleach==2.*
|
||||
raven
|
||||
babel
|
||||
django-i18nfield>=1.2.1
|
||||
django-hijack==2.1.*
|
||||
# Stripe
|
||||
|
||||
@@ -95,6 +95,7 @@ setup(
|
||||
'markdown',
|
||||
'bleach==2.*',
|
||||
'raven',
|
||||
'babel',
|
||||
'paypalrestsdk==1.12.*',
|
||||
'pycparser==2.13',
|
||||
'django-redis==4.7.*',
|
||||
|
||||
@@ -77,6 +77,36 @@ def test_perform_success(env, factory, monkeypatch):
|
||||
assert order.status == Order.STATUS_PAID
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_perform_success_zero_decimal_currency(env, factory, monkeypatch):
|
||||
event, order = env
|
||||
event.currency = 'JPY'
|
||||
event.save()
|
||||
|
||||
def charge_create(**kwargs):
|
||||
assert kwargs['amount'] == 13
|
||||
assert kwargs['currency'] == 'jpy'
|
||||
assert kwargs['source'] == 'tok_189fTT2eZvKYlo2CvJKzEzeu'
|
||||
c = MockedCharge()
|
||||
c.status = 'succeeded'
|
||||
c.paid = True
|
||||
return c
|
||||
|
||||
monkeypatch.setattr("stripe.Charge.create", charge_create)
|
||||
prov = StripeCC(event)
|
||||
req = factory.post('/', {
|
||||
'stripe_token': 'tok_189fTT2eZvKYlo2CvJKzEzeu',
|
||||
'stripe_last4': '4242',
|
||||
'stripe_brand': 'Visa'
|
||||
})
|
||||
req.session = {}
|
||||
prov.checkout_prepare(req, {})
|
||||
assert 'payment_stripe_token' in req.session
|
||||
prov.payment_perform(req, order)
|
||||
order.refresh_from_db()
|
||||
assert order.status == Order.STATUS_PAID
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_perform_card_error(env, factory, monkeypatch):
|
||||
event, order = env
|
||||
|
||||
@@ -1306,7 +1306,7 @@ class CheckoutTestCase(TestCase):
|
||||
target_status_code=200)
|
||||
response = self.client.get('/%s/%s/checkout/addons/' % (self.orga.slug, self.event.slug))
|
||||
assert 'Workshop 1' in response.rendered_content
|
||||
assert 'EUR 12.00' in response.rendered_content
|
||||
assert '€12.00' in response.rendered_content
|
||||
|
||||
def test_set_addons_included(self):
|
||||
ItemAddOn.objects.create(base_item=self.ticket, addon_category=self.workshopcat, min_count=1,
|
||||
@@ -1320,7 +1320,7 @@ class CheckoutTestCase(TestCase):
|
||||
self.assertRedirects(response, '/%s/%s/checkout/addons/' % (self.orga.slug, self.event.slug),
|
||||
target_status_code=200)
|
||||
assert 'Workshop 1' in response.rendered_content
|
||||
assert 'EUR 12.00' not in response.rendered_content
|
||||
assert '€12.00' not in response.rendered_content
|
||||
|
||||
def test_set_addons_subevent(self):
|
||||
self.event.has_subevents = True
|
||||
@@ -1340,7 +1340,7 @@ class CheckoutTestCase(TestCase):
|
||||
response = self.client.get('/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), follow=True)
|
||||
self.assertRedirects(response, '/%s/%s/checkout/addons/' % (self.orga.slug, self.event.slug),
|
||||
target_status_code=200)
|
||||
assert 'Workshop 1 (+ EUR 42.00)' in response.rendered_content
|
||||
assert 'Workshop 1 (+ €42.00)' in response.rendered_content
|
||||
|
||||
def test_set_addons_subevent_net_prices(self):
|
||||
self.event.has_subevents = True
|
||||
@@ -1365,8 +1365,8 @@ class CheckoutTestCase(TestCase):
|
||||
response = self.client.get('/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), follow=True)
|
||||
self.assertRedirects(response, '/%s/%s/checkout/addons/' % (self.orga.slug, self.event.slug),
|
||||
target_status_code=200)
|
||||
assert 'Workshop 1 (+ EUR 35.29 plus 19.00% VAT)' in response.rendered_content
|
||||
assert 'A (+ EUR 10.08 plus 19.00% VAT)' in response.rendered_content
|
||||
assert 'Workshop 1 (+ €35.29 plus 19.00% VAT)' in response.rendered_content
|
||||
assert 'A (+ €10.08 plus 19.00% VAT)' in response.rendered_content
|
||||
|
||||
def test_confirm_subevent_presale_not_yet(self):
|
||||
self.event.has_subevents = True
|
||||
|
||||
@@ -409,7 +409,7 @@ class OrdersTest(TestCase):
|
||||
'/%s/%s/order/%s/%s/pay/change' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret),
|
||||
)
|
||||
assert 'Test dummy' in response.rendered_content
|
||||
assert '+ 12.00' in response.rendered_content
|
||||
assert '+ €12.00' in response.rendered_content
|
||||
response = self.client.post(
|
||||
'/%s/%s/order/%s/%s/pay/change' % (self.orga.slug, self.event.slug, self.order.code, self.order.secret),
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user