diff --git a/src/pretix/base/models/tax.py b/src/pretix/base/models/tax.py index d496e41cb..b6b9ef36a 100644 --- a/src/pretix/base/models/tax.py +++ b/src/pretix/base/models/tax.py @@ -304,10 +304,21 @@ class TaxRule(LoggedModel): subtract_from_gross = Decimal('0.00') rate = adjust_rate + def _limit_subtract(base_price, subtract_from_gross): + if not subtract_from_gross: + return base_price + if base_price >= Decimal('0.00'): + # For positive prices, make sure they don't go negative because of bundles + return max(Decimal('0.00'), base_price - subtract_from_gross) + else: + # If the price is already negative, we don't really care any more + return base_price - subtract_from_gross + if rate == Decimal('0.00'): + gross = _limit_subtract(base_price, subtract_from_gross) return TaxedPrice( - net=max(Decimal('0.00'), base_price - subtract_from_gross), - gross=max(Decimal('0.00'), base_price - subtract_from_gross), + net=gross, + gross=gross, tax=Decimal('0.00'), rate=rate, name=self.name, @@ -320,19 +331,14 @@ class TaxRule(LoggedModel): base_price_is = 'net' if base_price_is == 'gross': - if base_price >= Decimal('0.00'): - # For positive prices, make sure they don't go negative because of bundles - gross = max(Decimal('0.00'), base_price - subtract_from_gross) - else: - # If the price is already negative, we don't really care any more - gross = base_price - subtract_from_gross + gross = _limit_subtract(base_price, subtract_from_gross) net = round_decimal(gross - (gross * (1 - 100 / (100 + rate))), currency) elif base_price_is == 'net': net = base_price gross = round_decimal((net * (1 + rate / 100)), currency) if subtract_from_gross: - gross -= subtract_from_gross + gross = _limit_subtract(gross, subtract_from_gross) net = round_decimal(gross - (gross * (1 - 100 / (100 + rate))), currency) else: diff --git a/src/tests/base/test_taxrules.py b/src/tests/base/test_taxrules.py index b643fd4ba..d54971ecd 100644 --- a/src/tests/base/test_taxrules.py +++ b/src/tests/base/test_taxrules.py @@ -782,3 +782,59 @@ def test_custom_rules_country_rate_subtract_from_gross(event): rate=Decimal('100.00'), name='', ) + + +@pytest.mark.django_db +def test_no_negative_due_to_subtract_from_gross(event): + tr = TaxRule( + event=event, + rate=Decimal("19.00"), + price_includes_tax=True, + ) + assert tr.tax(Decimal('100.00'), subtract_from_gross=Decimal('200.00')).gross == Decimal("0.00") + tr = TaxRule( + event=event, + rate=Decimal("0.00"), + price_includes_tax=True, + ) + assert tr.tax(Decimal('100.00'), subtract_from_gross=Decimal('200.00')).gross == Decimal("0.00") + tr = TaxRule( + event=event, + rate=Decimal("19.00"), + price_includes_tax=False, + ) + assert tr.tax(Decimal('100.00'), subtract_from_gross=Decimal('200.00')).gross == Decimal("0.00") + tr = TaxRule( + event=event, + rate=Decimal("19.00"), + price_includes_tax=True, + ) + assert tr.tax(Decimal('100.00'), subtract_from_gross=Decimal('200.00')).gross == Decimal("0.00") + + +@pytest.mark.django_db +def test_allow_negative(event): + tr = TaxRule( + event=event, + rate=Decimal("19.00"), + price_includes_tax=True, + ) + assert tr.tax(Decimal('-100.00')).gross == Decimal("-100.00") + tr = TaxRule( + event=event, + rate=Decimal("0.00"), + price_includes_tax=True, + ) + assert tr.tax(Decimal('-100.00')).gross == Decimal("-100.00") + tr = TaxRule( + event=event, + rate=Decimal("19.00"), + price_includes_tax=False, + ) + assert tr.tax(Decimal('-100.00')).gross == Decimal("-119.00") + tr = TaxRule( + event=event, + rate=Decimal("19.00"), + price_includes_tax=True, + ) + assert tr.tax(Decimal('-100.00')).gross == Decimal("-100.00")