diff --git a/src/pretix/base/models/items.py b/src/pretix/base/models/items.py index dc4479252a..56225077d1 100644 --- a/src/pretix/base/models/items.py +++ b/src/pretix/base/models/items.py @@ -11,6 +11,7 @@ from django.utils.functional import cached_property from django.utils.timezone import now from django.utils.translation import ugettext_lazy as _ +from pretix.base.decimal import round_decimal from pretix.base.i18n import I18nCharField, I18nTextField from pretix.base.models.base import LoggedModel @@ -221,6 +222,11 @@ class Item(LoggedModel): if self.event: self.event.get_cache().clear() + @property + def default_price_net(self): + tax_value = round_decimal(self.default_price * (1 - 100 / (100 + self.tax_rate))) + return self.default_price - tax_value + def is_available(self, now_dt: datetime=None) -> bool: """ Returns whether this item is available according to its ``active`` flag @@ -313,6 +319,11 @@ class ItemVariation(models.Model): def price(self): return self.default_price if self.default_price is not None else self.item.default_price + @property + def net_price(self): + tax_value = round_decimal(self.price * (1 - 100 / (100 + self.item.tax_rate))) + return self.price - tax_value + def delete(self, *args, **kwargs): super().delete(*args, **kwargs) if self.item: diff --git a/src/pretix/base/models/orders.py b/src/pretix/base/models/orders.py index 6096aaf897..17eb093b01 100644 --- a/src/pretix/base/models/orders.py +++ b/src/pretix/base/models/orders.py @@ -7,10 +7,11 @@ from typing import List, Union from django.conf import settings from django.db import models -from django.db.models import F +from django.db.models import F, Sum from django.db.models.signals import post_delete from django.dispatch import receiver from django.utils.crypto import get_random_string +from django.utils.functional import cached_property from django.utils.timezone import now from django.utils.translation import ugettext_lazy as _ @@ -215,6 +216,18 @@ class Order(LoggedModel): else: self.payment_fee_tax_value = Decimal('0.00') + @property + def payment_fee_net(self): + return self.payment_fee - self.payment_fee_tax_value + + @cached_property + def tax_total(self): + return (self.positions.aggregate(s=Sum('tax_value'))['s'] or 0) + self.payment_fee_tax_value + + @property + def net_total(self): + return self.total - self.tax_total + @staticmethod def normalize_code(code): tr = str.maketrans({ @@ -432,6 +445,10 @@ class AbstractPosition(models.Model): else: q.answer = "" + @property + def net_price(self): + return self.price - self.tax_value + class OrderPosition(AbstractPosition): """ diff --git a/src/pretix/base/services/cart.py b/src/pretix/base/services/cart.py index 3536b98e25..bbf953e115 100644 --- a/src/pretix/base/services/cart.py +++ b/src/pretix/base/services/cart.py @@ -9,6 +9,7 @@ from django.db.models import Q from django.utils.timezone import now from django.utils.translation import ugettext as _ +from pretix.base.decimal import round_decimal from pretix.base.i18n import LazyLocaleException from pretix.base.models import ( CartPosition, Event, Item, ItemVariation, Voucher, @@ -143,6 +144,8 @@ class CartManager: custom_price = Decimal(custom_price.replace(",", ".")) if custom_price > 100000000: return error_messages['price_too_high'] + if self.event.settings.display_net_prices: + custom_price = round_decimal(custom_price * (100 + item.tax_rate) / 100) price = max(custom_price, price) return price diff --git a/src/pretix/base/settings.py b/src/pretix/base/settings.py index 1d8756739f..5fa933b564 100644 --- a/src/pretix/base/settings.py +++ b/src/pretix/base/settings.py @@ -20,6 +20,10 @@ DEFAULTS = { 'default': '10', 'type': int }, + 'display_net_prices': { + 'default': 'False', + 'type': bool + }, 'attendee_names_asked': { 'default': 'True', 'type': bool diff --git a/src/pretix/control/forms/event.py b/src/pretix/control/forms/event.py index b2a091f5f0..4c39d2aae7 100644 --- a/src/pretix/control/forms/event.py +++ b/src/pretix/control/forms/event.py @@ -158,6 +158,12 @@ class EventSettingsForm(SettingsForm): help_text=_("Show item details before presale has started and after presale has ended"), required=False ) + display_net_prices = forms.BooleanField( + label=_("Show net prices instead of gross prices in the product list (not recommended!)"), + help_text=_("Independent of your choice, the cart will show gross prices as this the price that needs to be " + "paid"), + required=False + ) presale_start_show_date = forms.BooleanField( label=_("Show start date"), help_text=_("Show the presale start date before presale has started."), diff --git a/src/pretix/control/forms/orders.py b/src/pretix/control/forms/orders.py index 10af48350c..4c70b3c8b2 100644 --- a/src/pretix/control/forms/orders.py +++ b/src/pretix/control/forms/orders.py @@ -59,7 +59,7 @@ class OrderPositionChangeForm(forms.Form): price = forms.DecimalField( required=False, max_digits=10, decimal_places=2, - label=_('New price') + label=_('New price (gross)') ) operation = forms.ChoiceField( required=False, diff --git a/src/pretix/control/templates/pretixcontrol/event/settings.html b/src/pretix/control/templates/pretixcontrol/event/settings.html index 6e4cdc760b..b48e57d63b 100644 --- a/src/pretix/control/templates/pretixcontrol/event/settings.html +++ b/src/pretix/control/templates/pretixcontrol/event/settings.html @@ -25,6 +25,7 @@ {% bootstrap_field sform.contact_mail layout="horizontal" %} {% bootstrap_field sform.imprint_url layout="horizontal" %} {% bootstrap_field sform.show_quota_left layout="horizontal" %} + {% bootstrap_field sform.display_net_prices layout="horizontal" %}