From 17c2b40d1f9821d24b5716b7b80cb25e2b40c1d6 Mon Sep 17 00:00:00 2001 From: Richard Schreiber Date: Mon, 19 Sep 2022 17:33:27 +0200 Subject: [PATCH] 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'),