mirror of
https://github.com/pretix/pretix.git
synced 2026-05-06 15:24:02 +00:00
New data model for default tax rule and new options for cancellation fees (#4962)
* New data model for default tax rule * Remove misleading empty label when field is not optional * Allow to split cancellation fee * Fix API and tests * Update migration * Update src/tests/api/test_taxrules.py Co-authored-by: luelista <weller@rami.io> * Update src/tests/api/test_taxrules.py Co-authored-by: luelista <weller@rami.io> * Review note * Update src/pretix/base/models/tax.py Co-authored-by: luelista <weller@rami.io> * Flip API behaviour for default * Fix failing tests * Fix failing test * Split migration --------- Co-authored-by: luelista <weller@rami.io>
This commit is contained in:
@@ -2215,7 +2215,7 @@ class EventTest(TestCase):
|
||||
is_public=True,
|
||||
)
|
||||
event1.meta_values.create(property=prop, value="DE")
|
||||
tr7 = event1.tax_rules.create(rate=Decimal('7.00'))
|
||||
tr7 = event1.tax_rules.create(rate=Decimal('7.00'), default=True)
|
||||
c1 = event1.categories.create(name='Tickets')
|
||||
c2 = event1.categories.create(name='Workshops')
|
||||
i1 = event1.items.create(name='Foo', default_price=Decimal('13.00'), tax_rule=tr7,
|
||||
@@ -2228,7 +2228,6 @@ class EventTest(TestCase):
|
||||
que1 = event1.questions.create(question="Age", type="N")
|
||||
que1.items.add(i1)
|
||||
event1.settings.foo_setting = 23
|
||||
event1.settings.tax_rate_default = tr7
|
||||
cl1 = event1.checkin_lists.create(
|
||||
name="All", all_products=False,
|
||||
rules={
|
||||
@@ -2271,7 +2270,7 @@ class EventTest(TestCase):
|
||||
assert que1new.type == que1.type
|
||||
assert que1new.items.get(pk=i1new.pk)
|
||||
assert event2.settings.foo_setting == '23'
|
||||
assert event2.settings.tax_rate_default == trnew
|
||||
assert event2.cached_default_tax_rule == trnew
|
||||
assert event2.checkin_lists.count() == 1
|
||||
clnew = event2.checkin_lists.first()
|
||||
assert [i.pk for i in clnew.limit_products.all()] == [i1new.pk]
|
||||
|
||||
@@ -1117,7 +1117,7 @@ class OrderCancelTests(TestCase):
|
||||
self.order.save()
|
||||
self.order.payments.create(state=OrderPayment.PAYMENT_STATE_CONFIRMED, amount=48.5)
|
||||
with pytest.raises(OrderError):
|
||||
cancel_order(self.order.pk, cancellation_fee=50)
|
||||
cancel_order(self.order.pk, cancellation_fee=Decimal("50.00"))
|
||||
self.order.refresh_from_db()
|
||||
assert self.order.status == Order.STATUS_PAID
|
||||
assert self.order.total == 46
|
||||
@@ -1131,7 +1131,7 @@ class OrderCancelTests(TestCase):
|
||||
self.order.payments.create(state=OrderPayment.PAYMENT_STATE_CONFIRMED, amount=48.5)
|
||||
self.op1.voucher = self.event.vouchers.create(item=self.ticket, redeemed=1)
|
||||
self.op1.save()
|
||||
cancel_order(self.order.pk, cancellation_fee=2.5)
|
||||
cancel_order(self.order.pk, cancellation_fee=Decimal("2.50"))
|
||||
self.order.refresh_from_db()
|
||||
assert self.order.status == Order.STATUS_PAID
|
||||
self.op1.refresh_from_db()
|
||||
@@ -1158,7 +1158,7 @@ class OrderCancelTests(TestCase):
|
||||
self.order.payments.create(state=OrderPayment.PAYMENT_STATE_CONFIRMED, amount=48.5)
|
||||
self.op1.voucher = self.event.vouchers.create(item=self.ticket, redeemed=1)
|
||||
self.op1.save()
|
||||
cancel_order(self.order.pk, cancellation_fee=2.5)
|
||||
cancel_order(self.order.pk, cancellation_fee=Decimal("2.50"))
|
||||
self.order.refresh_from_db()
|
||||
assert self.order.status == Order.STATUS_PAID
|
||||
self.op1.refresh_from_db()
|
||||
@@ -1172,7 +1172,7 @@ class OrderCancelTests(TestCase):
|
||||
state=OrderPayment.PAYMENT_STATE_CONFIRMED,
|
||||
provider='testdummy_partialrefund'
|
||||
)
|
||||
cancel_order(self.order.pk, cancellation_fee=2, try_auto_refund=True)
|
||||
cancel_order(self.order.pk, cancellation_fee=Decimal("2.00"), try_auto_refund=True)
|
||||
r = self.order.refunds.get()
|
||||
assert r.state == OrderRefund.REFUND_STATE_DONE
|
||||
assert r.amount == Decimal('44.00')
|
||||
@@ -1190,7 +1190,7 @@ class OrderCancelTests(TestCase):
|
||||
provider='giftcard',
|
||||
info='{"gift_card": %d}' % gc.pk
|
||||
)
|
||||
cancel_order(self.order.pk, cancellation_fee=2, try_auto_refund=True)
|
||||
cancel_order(self.order.pk, cancellation_fee=Decimal("2.00"), try_auto_refund=True)
|
||||
r = self.order.refunds.get()
|
||||
assert r.state == OrderRefund.REFUND_STATE_DONE
|
||||
assert r.amount == Decimal('44.00')
|
||||
@@ -1209,7 +1209,7 @@ class OrderCancelTests(TestCase):
|
||||
state=OrderPayment.PAYMENT_STATE_CONFIRMED,
|
||||
provider='testdummy_partialrefund'
|
||||
)
|
||||
cancel_order(self.order.pk, cancellation_fee=2, try_auto_refund=True)
|
||||
cancel_order(self.order.pk, cancellation_fee=Decimal("2.00"), try_auto_refund=True)
|
||||
r = self.order.refunds.get()
|
||||
assert r.state == OrderRefund.REFUND_STATE_DONE
|
||||
assert gc.value == Decimal('0.00')
|
||||
@@ -1224,7 +1224,7 @@ class OrderCancelTests(TestCase):
|
||||
provider='testdummy_partialrefund'
|
||||
)
|
||||
with pytest.raises(OrderError):
|
||||
cancel_order(self.order.pk, cancellation_fee=2, try_auto_refund=True)
|
||||
cancel_order(self.order.pk, cancellation_fee=Decimal("2.00"), try_auto_refund=True)
|
||||
assert gc.value == Decimal('20.00')
|
||||
|
||||
@classscope(attr='o')
|
||||
@@ -1234,7 +1234,7 @@ class OrderCancelTests(TestCase):
|
||||
state=OrderPayment.PAYMENT_STATE_CONFIRMED,
|
||||
provider='testdummy_fullrefund'
|
||||
)
|
||||
cancel_order(self.order.pk, cancellation_fee=2, try_auto_refund=True)
|
||||
cancel_order(self.order.pk, cancellation_fee=Decimal("2.00"), try_auto_refund=True)
|
||||
assert not self.order.refunds.exists()
|
||||
assert self.order.all_logentries().filter(action_type='pretix.event.order.refund.requested').exists()
|
||||
|
||||
@@ -1258,7 +1258,7 @@ class OrderChangeManagerTests(TestCase):
|
||||
provider='banktransfer', state=OrderPayment.PAYMENT_STATE_CREATED, amount=self.order.total
|
||||
)
|
||||
self.tr7 = self.event.tax_rules.create(rate=Decimal('7.00'))
|
||||
self.tr19 = self.event.tax_rules.create(rate=Decimal('19.00'))
|
||||
self.tr19 = self.event.tax_rules.create(rate=Decimal('19.00'), default=True)
|
||||
self.ticket = Item.objects.create(event=self.event, name='Early-bird ticket', tax_rule=self.tr7,
|
||||
default_price=Decimal('23.00'), admission=True)
|
||||
self.ticket2 = Item.objects.create(event=self.event, name='Other ticket', tax_rule=self.tr7,
|
||||
@@ -1868,7 +1868,6 @@ class OrderChangeManagerTests(TestCase):
|
||||
|
||||
@classscope(attr='o')
|
||||
def test_payment_fee_calculation(self):
|
||||
self.event.settings.set('tax_rate_default', self.tr19.pk)
|
||||
prov = self.ocm._get_payment_provider()
|
||||
prov.settings.set('_fee_abs', Decimal('0.30'))
|
||||
self.ocm.change_price(self.op1, Decimal('24.00'))
|
||||
@@ -1882,7 +1881,6 @@ class OrderChangeManagerTests(TestCase):
|
||||
|
||||
@classscope(attr='o')
|
||||
def test_pending_free_order_stays_pending(self):
|
||||
self.event.settings.set('tax_rate_default', self.tr19.pk)
|
||||
self.ocm.change_price(self.op1, Decimal('0.00'))
|
||||
self.ocm.change_price(self.op2, Decimal('0.00'))
|
||||
self.ocm.commit()
|
||||
@@ -2270,7 +2268,6 @@ class OrderChangeManagerTests(TestCase):
|
||||
|
||||
@classscope(attr='o')
|
||||
def test_recalculate_country_rate(self):
|
||||
self.event.settings.set('tax_rate_default', self.tr19.pk)
|
||||
prov = self.ocm._get_payment_provider()
|
||||
prov.settings.set('_fee_abs', Decimal('0.30'))
|
||||
self.ocm._recalculate_total_and_payment_fee()
|
||||
@@ -2303,7 +2300,6 @@ class OrderChangeManagerTests(TestCase):
|
||||
|
||||
@classscope(attr='o')
|
||||
def test_recalculate_country_rate_keep_gross(self):
|
||||
self.event.settings.set('tax_rate_default', self.tr19.pk)
|
||||
prov = self.ocm._get_payment_provider()
|
||||
prov.settings.set('_fee_abs', Decimal('0.30'))
|
||||
self.ocm._recalculate_total_and_payment_fee()
|
||||
@@ -2334,7 +2330,6 @@ class OrderChangeManagerTests(TestCase):
|
||||
|
||||
@classscope(attr='o')
|
||||
def test_recalculate_reverse_charge(self):
|
||||
self.event.settings.set('tax_rate_default', self.tr19.pk)
|
||||
prov = self.ocm._get_payment_provider()
|
||||
prov.settings.set('_fee_abs', Decimal('0.30'))
|
||||
self.ocm._recalculate_total_and_payment_fee()
|
||||
@@ -2493,7 +2488,6 @@ class OrderChangeManagerTests(TestCase):
|
||||
@classscope(attr='o')
|
||||
def test_split_pending_payment_fees(self):
|
||||
# Set payment fees
|
||||
self.event.settings.set('tax_rate_default', self.tr19.pk)
|
||||
prov = self.ocm._get_payment_provider()
|
||||
prov.settings.set('_fee_percent', Decimal('2.00'))
|
||||
prov.settings.set('_fee_abs', Decimal('1.00'))
|
||||
@@ -2697,7 +2691,6 @@ class OrderChangeManagerTests(TestCase):
|
||||
ia = self._enable_reverse_charge()
|
||||
|
||||
# Set payment fees
|
||||
self.event.settings.set('tax_rate_default', self.tr19.pk)
|
||||
prov = self.ocm._get_payment_provider()
|
||||
prov.settings.set('_fee_percent', Decimal('2.00'))
|
||||
prov.settings.set('_fee_reverse_calc', False)
|
||||
@@ -2791,7 +2784,6 @@ class OrderChangeManagerTests(TestCase):
|
||||
@classscope(attr='o')
|
||||
def test_split_paid_payment_fees(self):
|
||||
# Set payment fees
|
||||
self.event.settings.set('tax_rate_default', self.tr19.pk)
|
||||
prov = self.ocm._get_payment_provider()
|
||||
prov.settings.set('_fee_percent', Decimal('2.00'))
|
||||
prov.settings.set('_fee_abs', Decimal('1.00'))
|
||||
|
||||
@@ -27,8 +27,11 @@ from django.utils.timezone import now
|
||||
from django_countries.fields import Country
|
||||
from django_scopes import scope
|
||||
|
||||
from pretix.base.models import Event, InvoiceAddress, Organizer, TaxRule
|
||||
from pretix.base.models import (
|
||||
Event, InvoiceAddress, OrderFee, OrderPosition, Organizer, TaxRule,
|
||||
)
|
||||
from pretix.base.models.tax import TaxedPrice
|
||||
from pretix.base.services.tax import split_fee_for_taxes
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@@ -962,3 +965,39 @@ def test_allow_negative(event):
|
||||
price_includes_tax=True,
|
||||
)
|
||||
assert tr.tax(Decimal('-100.00')).gross == Decimal("-100.00")
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_split_fees(event):
|
||||
tr19 = TaxRule(rate=Decimal("19.00"), pk=1)
|
||||
tr7 = TaxRule(rate=Decimal("7.00"), pk=2)
|
||||
item = event.items.create(name="Budget Ticket", default_price=23)
|
||||
|
||||
op1 = OrderPosition(price=Decimal("11.90"), item=item)
|
||||
op1._calculate_tax(tax_rule=tr19, invoice_address=InvoiceAddress())
|
||||
op2 = OrderPosition(price=Decimal("10.70"), item=item)
|
||||
op2._calculate_tax(tax_rule=tr7, invoice_address=InvoiceAddress())
|
||||
of1 = OrderFee(value=Decimal("5.00"), fee_type=OrderFee.FEE_TYPE_SHIPPING)
|
||||
of1._calculate_tax(tax_rule=tr7, invoice_address=InvoiceAddress())
|
||||
|
||||
# Example of a 10% service fee
|
||||
assert split_fee_for_taxes([op1, op2], Decimal("2.26"), event) == [
|
||||
(tr7, Decimal("1.07")),
|
||||
(tr19, Decimal("1.19")),
|
||||
]
|
||||
|
||||
# Example of a full cancellation fee
|
||||
assert split_fee_for_taxes([op1, op2], Decimal("22.60"), event) == [
|
||||
(tr7, Decimal("10.70")),
|
||||
(tr19, Decimal("11.90")),
|
||||
]
|
||||
assert split_fee_for_taxes([op1, op2, of1], Decimal("27.60"), event) == [
|
||||
(tr7, Decimal("15.70")),
|
||||
(tr19, Decimal("11.90")),
|
||||
]
|
||||
|
||||
# Example that rounding always is done with benefit to the highest tax rate
|
||||
assert split_fee_for_taxes([op1, op2], Decimal("0.03"), event) == [
|
||||
(tr7, Decimal("0.01")),
|
||||
(tr19, Decimal("0.02")),
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user