From 17c2b40d1f9821d24b5716b7b80cb25e2b40c1d6 Mon Sep 17 00:00:00 2001 From: Richard Schreiber Date: Mon, 19 Sep 2022 17:33:27 +0200 Subject: [PATCH 1/3] add option to split taxes --- pretix_servicefees/signals.py | 48 ++++++++++++++++++- .../pretix_servicefees/settings.html | 1 + pretix_servicefees/views.py | 5 ++ 3 files changed, 53 insertions(+), 1 deletion(-) diff --git a/pretix_servicefees/signals.py b/pretix_servicefees/signals.py index 2645534..c81aa61 100644 --- a/pretix_servicefees/signals.py +++ b/pretix_servicefees/signals.py @@ -1,3 +1,4 @@ +from collections import defaultdict from decimal import Decimal from django.urls import resolve, reverse @@ -5,7 +6,7 @@ from django.dispatch import receiver from django.http import HttpRequest from django.utils.translation import gettext_lazy as _, gettext, get_language from pretix.base.decimal import round_decimal -from pretix.base.models import Event, Order, TaxRule +from pretix.base.models import CartPosition, Event, Order, TaxRule from pretix.base.models.orders import OrderFee from pretix.base.settings import settings_hierarkey from pretix.base.signals import order_fee_calculation @@ -76,7 +77,52 @@ def get_fees(event, total, invoice_address, mod='', request=None, positions=[], summed += fval if (fee_per_ticket or fee_abs or fee_percent) and total != Decimal('0.00'): + tax_rule_zero = TaxRule.zero() fee = round_decimal(fee_abs + total * (fee_percent / 100) + len(positions) * fee_per_ticket, event.currency) + split_taxes = event.settings.get('service_fee_split_taxes', as_type=bool) + if split_taxes: + # split taxes based on products ordered + d = defaultdict(lambda: Decimal('0.00')) + for p in positions: + if isinstance(p, CartPosition): + tr = p.item.tax_rule + else: + tr = p.tax_rule + d[tr] += p.price - p.tax_value + + base_values = sorted([tuple(t) for t in d.items()], key=lambda t: t[0].rate) + sum_base = sum(t[1] for t in base_values) + if sum_base: + fee_values = [(t[0], round_decimal(fee * t[1] / sum_base, event.currency)) + for t in base_values] + sum_fee = sum(t[1] for t in fee_values) + + # If there are rounding differences, we fix them up, but always leaning to the benefit of the tax + # authorities + if sum_fee > fee: + fee_values[0] = (fee_values[0][0], fee_values[0][1] + (fee - sum_fee)) + elif sum_fee < fee: + fee_values[-1] = (fee_values[-1][0], fee_values[-1][1] + (fee - sum_fee)) + else: + fee_values = [(event.settings.tax_rate_default or tax_rule_zero, fee)] + + else: + fee_values = [(event.settings.tax_rate_default or tax_rule_zero, fee)] + + fees = [] + for tax_rule, price in fee_values: + tax_rule = tax_rule or tax_rule_zero + tax = tax_rule.tax(price, invoice_address=invoice_address, base_price_is='gross') + fees.append(OrderFee( + fee_type=OrderFee.FEE_TYPE_SERVICE, + internal_type='', + value=price, + tax_rate=tax.rate, + tax_value=tax.tax, + tax_rule=tax_rule + )) + return fees + tax_rule = event.settings.tax_rate_default or TaxRule.zero() tax = tax_rule.tax(fee, invoice_address=invoice_address, base_price_is='gross') return [OrderFee( diff --git a/pretix_servicefees/templates/pretix_servicefees/settings.html b/pretix_servicefees/templates/pretix_servicefees/settings.html index 642e6be..1160621 100644 --- a/pretix_servicefees/templates/pretix_servicefees/settings.html +++ b/pretix_servicefees/templates/pretix_servicefees/settings.html @@ -14,6 +14,7 @@ {% bootstrap_field form.service_fee_skip_free layout="control" %} {% bootstrap_field form.service_fee_abs addon_after=request.event.currency layout="control" %} {% bootstrap_field form.service_fee_percent addon_after="%" layout="control" %} + {% bootstrap_field form.service_fee_split_taxes layout="control" %} {% bootstrap_field form.service_fee_skip_if_gift_card layout="control" %}
diff --git a/pretix_servicefees/views.py b/pretix_servicefees/views.py index 1afbc4d..41f67fb 100644 --- a/pretix_servicefees/views.py +++ b/pretix_servicefees/views.py @@ -46,6 +46,11 @@ class ServiceFeeSettingsForm(SettingsForm): help_text=_('Note that regardless of this setting, a per-ticket fee will not be charged if the entire order is free.'), required=False ) + service_fee_split_taxes = forms.BooleanField( + label=_('Split taxes proportionate to the tax rates and net values of the ordered products.'), + help_text=_('If not split based on ordered products, the tax rate falls back to the event’s base tax rate or no tax, if none is given.'), + required=False + ) service_fee_abs_resellers = forms.DecimalField( label=_('Fixed fee per order'), From ce6bc3a0f5d9c69b76390349ef8abb5c06e95093 Mon Sep 17 00:00:00 2001 From: Richard Schreiber Date: Mon, 19 Sep 2022 17:33:51 +0200 Subject: [PATCH 2/3] fix isort --- pretix_servicefees/signals.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/pretix_servicefees/signals.py b/pretix_servicefees/signals.py index c81aa61..49a7538 100644 --- a/pretix_servicefees/signals.py +++ b/pretix_servicefees/signals.py @@ -1,10 +1,9 @@ from collections import defaultdict from decimal import Decimal - -from django.urls import resolve, reverse from django.dispatch import receiver from django.http import HttpRequest -from django.utils.translation import gettext_lazy as _, gettext, get_language +from django.urls import resolve, reverse +from django.utils.translation import get_language, gettext, gettext_lazy as _ from pretix.base.decimal import round_decimal from pretix.base.models import CartPosition, Event, Order, TaxRule from pretix.base.models.orders import OrderFee @@ -12,7 +11,9 @@ from pretix.base.settings import settings_hierarkey from pretix.base.signals import order_fee_calculation from pretix.base.templatetags.money import money_filter from pretix.control.signals import nav_event_settings -from pretix.presale.signals import fee_calculation_for_cart, front_page_top, order_meta_from_request +from pretix.presale.signals import ( + fee_calculation_for_cart, front_page_top, order_meta_from_request, +) from pretix.presale.views import get_cart from pretix.presale.views.cart import cart_session @@ -140,7 +141,9 @@ def get_fees(event, total, invoice_address, mod='', request=None, positions=[], def cart_fee(sender: Event, request: HttpRequest, invoice_address, total, **kwargs): mod = '' try: - from pretix_resellers.utils import ResellerException, get_reseller_and_user + from pretix_resellers.utils import ( + ResellerException, get_reseller_and_user, + ) except ImportError: pass else: @@ -199,7 +202,9 @@ def front_page_top_recv(sender: Event, **kwargs): def order_meta_signal(sender: Event, request: HttpRequest, **kwargs): meta = {} try: - from pretix_resellers.utils import ResellerException, get_reseller_and_user + from pretix_resellers.utils import ( + ResellerException, get_reseller_and_user, + ) except ImportError: pass else: From a4683de6449242f80590d54e6a0d1f04437d6d28 Mon Sep 17 00:00:00 2001 From: Richard Schreiber Date: Mon, 19 Sep 2022 17:35:23 +0200 Subject: [PATCH 3/3] remove dead code --- pretix_servicefees/signals.py | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/pretix_servicefees/signals.py b/pretix_servicefees/signals.py index 49a7538..a362087 100644 --- a/pretix_servicefees/signals.py +++ b/pretix_servicefees/signals.py @@ -124,16 +124,6 @@ def get_fees(event, total, invoice_address, mod='', request=None, positions=[], )) return fees - tax_rule = event.settings.tax_rate_default or TaxRule.zero() - tax = tax_rule.tax(fee, invoice_address=invoice_address, base_price_is='gross') - return [OrderFee( - fee_type=OrderFee.FEE_TYPE_SERVICE, - internal_type='', - value=fee, - tax_rate=tax.rate, - tax_value=tax.tax, - tax_rule=tax_rule - )] return []