mirror of
https://github.com/pretix/pretix.git
synced 2026-05-03 14:54:04 +00:00
@@ -1,8 +1,10 @@
|
||||
from datetime import timedelta
|
||||
import json
|
||||
from datetime import date, timedelta
|
||||
from decimal import Decimal
|
||||
|
||||
import pytest
|
||||
from django.db import DatabaseError
|
||||
from django.core.serializers.json import DjangoJSONEncoder
|
||||
from django.db import DatabaseError, transaction
|
||||
from django.utils.timezone import now
|
||||
from django_countries.fields import Country
|
||||
|
||||
@@ -14,6 +16,8 @@ from pretix.base.services.invoices import (
|
||||
build_preview_invoice_pdf, generate_cancellation, generate_invoice,
|
||||
invoice_pdf_task, regenerate_invoice,
|
||||
)
|
||||
from pretix.base.services.orders import OrderChangeManager
|
||||
from pretix.base.settings import GlobalSettingsObject
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@@ -23,19 +27,20 @@ def env():
|
||||
organizer=o, name='Dummy', slug='dummy',
|
||||
date_from=now(), plugins='pretix.plugins.banktransfer'
|
||||
)
|
||||
tr = event.tax_rules.create(rate=Decimal('19.00'))
|
||||
o = Order.objects.create(
|
||||
code='FOO', event=event, email='dummy@dummy.test',
|
||||
status=Order.STATUS_PENDING,
|
||||
datetime=now(), expires=now() + timedelta(days=10),
|
||||
total=0, payment_provider='banktransfer',
|
||||
payment_fee=Decimal('0.25'), payment_fee_tax_rate=0,
|
||||
payment_fee_tax_value=0, locale='en'
|
||||
payment_fee=Decimal('0.25'), payment_fee_tax_rate=Decimal('19.00'),
|
||||
payment_fee_tax_value=Decimal('0.04'), locale='en', payment_fee_tax_rule=tr
|
||||
)
|
||||
ticket = Item.objects.create(event=event, name='Early-bird ticket',
|
||||
category=None, default_price=23,
|
||||
category=None, default_price=23, tax_rule=tr,
|
||||
admission=True)
|
||||
t_shirt = Item.objects.create(event=event, name='T-Shirt',
|
||||
category=None, default_price=42,
|
||||
category=None, default_price=42, tax_rule=tr,
|
||||
admission=True)
|
||||
variation = ItemVariation.objects.create(value='M', item=t_shirt)
|
||||
OrderPosition.objects.create(
|
||||
@@ -52,6 +57,22 @@ def env():
|
||||
price=Decimal("42.00"),
|
||||
positionid=2,
|
||||
)
|
||||
gs = GlobalSettingsObject()
|
||||
gs.settings.ecb_rates_date = date.today()
|
||||
gs.settings.ecb_rates_dict = json.dumps({
|
||||
"USD": "1.1648",
|
||||
"RON": "4.5638",
|
||||
"CZK": "26.024",
|
||||
"BGN": "1.9558",
|
||||
"HRK": "7.4098",
|
||||
"EUR": "1.0000",
|
||||
"NOK": "9.3525",
|
||||
"HUF": "305.15",
|
||||
"DKK": "7.4361",
|
||||
"PLN": "4.2408",
|
||||
"GBP": "0.89350",
|
||||
"SEK": "9.5883"
|
||||
}, cls=DjangoJSONEncoder)
|
||||
return event, o
|
||||
|
||||
|
||||
@@ -116,6 +137,59 @@ def test_positions_skip_free(env):
|
||||
assert inv.lines.count() == 2
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_reverse_charge_note(env):
|
||||
event, order = env
|
||||
|
||||
tr = event.tax_rules.first()
|
||||
tr.eu_reverse_charge = True
|
||||
tr.home_country = Country('DE')
|
||||
tr.save()
|
||||
|
||||
event.settings.set('invoice_language', 'en')
|
||||
InvoiceAddress.objects.create(company='Acme Company', street='221B Baker Street', zipcode='12345', city='Warsaw',
|
||||
country=Country('PL'), vat_id='PL123456780', vat_id_validated=True, order=order,
|
||||
is_business=True)
|
||||
|
||||
ocm = OrderChangeManager(order, None)
|
||||
ocm.recalculate_taxes()
|
||||
ocm.commit()
|
||||
assert not order.positions.filter(tax_value__gt=0).exists()
|
||||
|
||||
inv = generate_invoice(order)
|
||||
assert "reverse charge" in inv.additional_text.lower()
|
||||
assert inv.foreign_currency_display == "PLN"
|
||||
assert inv.foreign_currency_rate == Decimal("4.2408")
|
||||
assert inv.foreign_currency_rate_date == date.today()
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_reverse_charge_foreign_currency_data_too_old(env):
|
||||
event, order = env
|
||||
gs = GlobalSettingsObject()
|
||||
gs.settings.ecb_rates_date = date.today() - timedelta(days=14)
|
||||
|
||||
tr = event.tax_rules.first()
|
||||
tr.eu_reverse_charge = True
|
||||
tr.home_country = Country('DE')
|
||||
tr.save()
|
||||
|
||||
event.settings.set('invoice_language', 'en')
|
||||
InvoiceAddress.objects.create(company='Acme Company', street='221B Baker Street', zipcode='12345', city='Warsaw',
|
||||
country=Country('PL'), vat_id='PL123456780', vat_id_validated=True, order=order,
|
||||
is_business=True)
|
||||
|
||||
ocm = OrderChangeManager(order, None)
|
||||
ocm.recalculate_taxes()
|
||||
ocm.commit()
|
||||
assert not order.positions.filter(tax_value__gt=0).exists()
|
||||
|
||||
inv = generate_invoice(order)
|
||||
assert "reverse charge" in inv.additional_text.lower()
|
||||
assert inv.foreign_currency_rate is None
|
||||
assert inv.foreign_currency_rate_date is None
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_positions(env):
|
||||
event, order = env
|
||||
@@ -279,11 +353,12 @@ def test_invoice_number_prefixes(env):
|
||||
|
||||
# Test database uniqueness check
|
||||
with pytest.raises(DatabaseError):
|
||||
Invoice.objects.create(
|
||||
order=order,
|
||||
event=order.event,
|
||||
organizer=order.event.organizer,
|
||||
date=now().date(),
|
||||
locale='en',
|
||||
invoice_no='00001',
|
||||
)
|
||||
with transaction.atomic():
|
||||
Invoice.objects.create(
|
||||
order=order,
|
||||
event=order.event,
|
||||
organizer=order.event.organizer,
|
||||
date=now().date(),
|
||||
locale='en',
|
||||
invoice_no='00001',
|
||||
)
|
||||
|
||||
@@ -935,6 +935,56 @@ class EventTest(TestCase):
|
||||
|
||||
self.assertIn('slug', str(context.exception))
|
||||
|
||||
def test_copy(self):
|
||||
event1 = Event.objects.create(
|
||||
organizer=self.organizer, name='Download', slug='ab1234',
|
||||
date_from=datetime.datetime(2013, 12, 26, tzinfo=datetime.timezone.utc),
|
||||
is_public=True,
|
||||
)
|
||||
tr7 = event1.tax_rules.create(rate=Decimal('7.00'))
|
||||
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,
|
||||
category=c1)
|
||||
v1 = i1.variations.create(value='Bar')
|
||||
i1.addons.create(addon_category=c2)
|
||||
q1 = event1.quotas.create(name='Quota 1', size=50)
|
||||
q1.items.add(i1)
|
||||
q1.variations.add(v1)
|
||||
que1 = event1.questions.create(question="Age", type="N")
|
||||
que1.items.add(i1)
|
||||
event1.settings.foo_setting = 23
|
||||
event1.settings.tax_rate_default = tr7
|
||||
|
||||
event2 = Event.objects.create(
|
||||
organizer=self.organizer, name='Download', slug='ab1234',
|
||||
date_from=datetime.datetime(2013, 12, 26, tzinfo=datetime.timezone.utc)
|
||||
)
|
||||
event2.copy_data_from(event1)
|
||||
|
||||
for a in (tr7, c1, c2, i1, q1, que1):
|
||||
a.refresh_from_db()
|
||||
assert a.event == event1
|
||||
|
||||
trnew = event2.tax_rules.first()
|
||||
assert trnew.rate == tr7.rate
|
||||
c1new = event2.categories.get(name='Tickets')
|
||||
c2new = event2.categories.get(name='Workshops')
|
||||
i1new = event2.items.first()
|
||||
assert i1new.name == i1.name
|
||||
assert i1new.category == c1new
|
||||
assert i1new.tax_rule == trnew
|
||||
assert i1new.variations.count() == 1
|
||||
assert i1new.addons.get(addon_category=c2new)
|
||||
q1new = event2.quotas.first()
|
||||
assert q1new.size == q1.size
|
||||
assert q1new.items.get(pk=i1new.pk)
|
||||
que1new = event2.questions.first()
|
||||
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
|
||||
|
||||
|
||||
class SubEventTest(TestCase):
|
||||
@classmethod
|
||||
|
||||
@@ -6,10 +6,11 @@ import pytz
|
||||
from django.core import mail as djmail
|
||||
from django.test import TestCase
|
||||
from django.utils.timezone import make_aware, now
|
||||
from django_countries.fields import Country
|
||||
|
||||
from pretix.base.decimal import round_decimal
|
||||
from pretix.base.models import (
|
||||
CartPosition, Event, Item, Order, OrderPosition, Organizer,
|
||||
CartPosition, Event, InvoiceAddress, Item, Order, OrderPosition, Organizer,
|
||||
)
|
||||
from pretix.base.models.items import SubEventItem
|
||||
from pretix.base.payment import FreeOrderProvider
|
||||
@@ -102,7 +103,8 @@ def test_expiry_last_relative_subevents(event):
|
||||
event.date_from = now() + timedelta(days=5)
|
||||
event.has_subevents = True
|
||||
event.save()
|
||||
ticket = Item.objects.create(event=event, name='Early-bird ticket', tax_rate=Decimal('7.00'),
|
||||
tr7 = event.tax_rules.create(rate=Decimal('17.00'))
|
||||
ticket = Item.objects.create(event=event, name='Early-bird ticket', tax_rule=tr7,
|
||||
default_price=Decimal('23.00'), admission=True)
|
||||
|
||||
se1 = event.subevents.create(name="SE1", date_from=now() + timedelta(days=10))
|
||||
@@ -196,7 +198,7 @@ class DownloadReminderTests(TestCase):
|
||||
expires=now() + timedelta(days=10),
|
||||
total=Decimal('46.00'), payment_provider='banktransfer'
|
||||
)
|
||||
self.ticket = Item.objects.create(event=self.event, name='Early-bird ticket', tax_rate=Decimal('7.00'),
|
||||
self.ticket = Item.objects.create(event=self.event, name='Early-bird ticket',
|
||||
default_price=Decimal('23.00'), admission=True)
|
||||
self.op1 = OrderPosition.objects.create(
|
||||
order=self.order, item=self.ticket, variation=None,
|
||||
@@ -233,18 +235,21 @@ class OrderChangeManagerTests(TestCase):
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
o = Organizer.objects.create(name='Dummy', slug='dummy')
|
||||
self.event = Event.objects.create(organizer=o, name='Dummy', slug='dummy', date_from=now(), plugins='pretix.plugins.banktransfer')
|
||||
self.event = Event.objects.create(organizer=o, name='Dummy', slug='dummy', date_from=now(),
|
||||
plugins='pretix.plugins.banktransfer')
|
||||
self.order = Order.objects.create(
|
||||
code='FOO', event=self.event, email='dummy@dummy.test',
|
||||
status=Order.STATUS_PENDING, locale='en',
|
||||
datetime=now(), expires=now() + timedelta(days=10),
|
||||
total=Decimal('46.00'), payment_provider='banktransfer'
|
||||
)
|
||||
self.ticket = Item.objects.create(event=self.event, name='Early-bird ticket', tax_rate=Decimal('7.00'),
|
||||
self.tr7 = self.event.tax_rules.create(rate=Decimal('7.00'))
|
||||
self.tr19 = self.event.tax_rules.create(rate=Decimal('19.00'))
|
||||
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_rate=Decimal('7.00'),
|
||||
self.ticket2 = Item.objects.create(event=self.event, name='Other ticket', tax_rule=self.tr7,
|
||||
default_price=Decimal('23.00'), admission=True)
|
||||
self.shirt = Item.objects.create(event=self.event, name='T-Shirt', tax_rate=Decimal('19.00'),
|
||||
self.shirt = Item.objects.create(event=self.event, name='T-Shirt', tax_rule=self.tr19,
|
||||
default_price=Decimal('12.00'))
|
||||
self.op1 = OrderPosition.objects.create(
|
||||
order=self.order, item=self.ticket, variation=None,
|
||||
@@ -260,6 +265,18 @@ class OrderChangeManagerTests(TestCase):
|
||||
self.quota.items.add(self.ticket2)
|
||||
self.quota.items.add(self.shirt)
|
||||
|
||||
def _enable_reverse_charge(self):
|
||||
self.tr7.eu_reverse_charge = True
|
||||
self.tr7.home_country = Country('DE')
|
||||
self.tr7.save()
|
||||
self.tr19.eu_reverse_charge = True
|
||||
self.tr19.home_country = Country('DE')
|
||||
self.tr19.save()
|
||||
return InvoiceAddress.objects.create(
|
||||
order=self.order, is_business=True, vat_id='ATU1234567', vat_id_validated=True,
|
||||
country=Country('AT')
|
||||
)
|
||||
|
||||
def test_change_subevent_quota_required(self):
|
||||
self.event.has_subevents = True
|
||||
self.event.save()
|
||||
@@ -291,6 +308,48 @@ class OrderChangeManagerTests(TestCase):
|
||||
assert self.op1.price == 12
|
||||
assert self.order.total == self.op1.price + self.op2.price
|
||||
|
||||
def test_change_subevent_reverse_charge(self):
|
||||
self._enable_reverse_charge()
|
||||
self.event.has_subevents = True
|
||||
self.event.save()
|
||||
se1 = self.event.subevents.create(name="Foo", date_from=now())
|
||||
se2 = self.event.subevents.create(name="Bar", date_from=now())
|
||||
SubEventItem.objects.create(subevent=se2, item=self.ticket, price=10.7)
|
||||
self.op1.subevent = se1
|
||||
self.op1.save()
|
||||
self.quota.subevent = se2
|
||||
self.quota.save()
|
||||
|
||||
self.ocm.change_subevent(self.op1, se2)
|
||||
self.ocm.commit()
|
||||
self.op1.refresh_from_db()
|
||||
self.order.refresh_from_db()
|
||||
assert self.op1.subevent == se2
|
||||
assert self.op1.price == Decimal('10.00')
|
||||
assert self.op1.tax_value == Decimal('0.00')
|
||||
assert self.order.total == self.op1.price + self.op2.price
|
||||
|
||||
def test_change_subevent_net_price(self):
|
||||
self.event.has_subevents = True
|
||||
self.event.save()
|
||||
se1 = self.event.subevents.create(name="Foo", date_from=now())
|
||||
se2 = self.event.subevents.create(name="Bar", date_from=now())
|
||||
self.tr7.price_includes_tax = False
|
||||
self.tr7.save()
|
||||
SubEventItem.objects.create(subevent=se2, item=self.ticket, price=10)
|
||||
self.op1.subevent = se1
|
||||
self.op1.save()
|
||||
self.quota.subevent = se2
|
||||
self.quota.save()
|
||||
|
||||
self.ocm.change_subevent(self.op1, se2)
|
||||
self.ocm.commit()
|
||||
self.op1.refresh_from_db()
|
||||
self.order.refresh_from_db()
|
||||
assert self.op1.subevent == se2
|
||||
assert self.op1.price == Decimal('10.70')
|
||||
assert self.order.total == self.op1.price + self.op2.price
|
||||
|
||||
def test_change_subevent_sold_out(self):
|
||||
self.event.has_subevents = True
|
||||
self.event.save()
|
||||
@@ -320,10 +379,35 @@ class OrderChangeManagerTests(TestCase):
|
||||
self.order.refresh_from_db()
|
||||
assert self.op1.item == self.shirt
|
||||
assert self.op1.price == self.shirt.default_price
|
||||
assert self.op1.tax_rate == self.shirt.tax_rate
|
||||
assert self.op1.tax_rate == self.shirt.tax_rule.rate
|
||||
assert round_decimal(self.op1.price * (1 - 100 / (100 + self.op1.tax_rate))) == self.op1.tax_value
|
||||
assert self.order.total == self.op1.price + self.op2.price
|
||||
|
||||
def test_change_item_net_price_success(self):
|
||||
self.tr19.price_includes_tax = False
|
||||
self.tr19.save()
|
||||
self.ocm.change_item(self.op1, self.shirt, None)
|
||||
self.ocm.commit()
|
||||
self.op1.refresh_from_db()
|
||||
self.order.refresh_from_db()
|
||||
assert self.op1.item == self.shirt
|
||||
assert self.op1.price == Decimal('14.28')
|
||||
assert self.op1.tax_rate == self.shirt.tax_rule.rate
|
||||
assert round_decimal(self.op1.price * (1 - 100 / (100 + self.op1.tax_rate))) == self.op1.tax_value
|
||||
assert self.order.total == self.op1.price + self.op2.price
|
||||
|
||||
def test_change_item_reverse_charge(self):
|
||||
self._enable_reverse_charge()
|
||||
self.ocm.change_item(self.op1, self.shirt, None)
|
||||
self.ocm.commit()
|
||||
self.op1.refresh_from_db()
|
||||
self.order.refresh_from_db()
|
||||
assert self.op1.item == self.shirt
|
||||
assert self.op1.price == Decimal('10.08')
|
||||
assert self.op1.tax_rate == Decimal('0.00')
|
||||
assert self.op1.tax_value == Decimal('0.00')
|
||||
assert self.order.total == self.op1.price + self.op2.price
|
||||
|
||||
def test_change_price_success(self):
|
||||
self.ocm.change_price(self.op1, Decimal('24.00'))
|
||||
self.ocm.commit()
|
||||
@@ -334,6 +418,18 @@ class OrderChangeManagerTests(TestCase):
|
||||
assert round_decimal(self.op1.price * (1 - 100 / (100 + self.op1.tax_rate))) == self.op1.tax_value
|
||||
assert self.order.total == self.op1.price + self.op2.price
|
||||
|
||||
def test_change_price_net_success(self):
|
||||
self.tr7.price_includes_tax = False
|
||||
self.tr7.save()
|
||||
self.ocm.change_price(self.op1, Decimal('10.00'))
|
||||
self.ocm.commit()
|
||||
self.op1.refresh_from_db()
|
||||
self.order.refresh_from_db()
|
||||
assert self.op1.item == self.ticket
|
||||
assert self.op1.price == Decimal('10.70')
|
||||
assert round_decimal(self.op1.price * (1 - 100 / (100 + self.op1.tax_rate))) == self.op1.tax_value
|
||||
assert self.order.total == self.op1.price + self.op2.price
|
||||
|
||||
def test_cancel_success(self):
|
||||
self.ocm.cancel(self.op1)
|
||||
self.ocm.commit()
|
||||
@@ -436,7 +532,7 @@ class OrderChangeManagerTests(TestCase):
|
||||
assert self.op2.item == self.ticket
|
||||
|
||||
def test_payment_fee_calculation(self):
|
||||
self.event.settings.set('tax_rate_default', Decimal('19.00'))
|
||||
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'))
|
||||
@@ -445,7 +541,8 @@ class OrderChangeManagerTests(TestCase):
|
||||
assert self.order.total == Decimal('47.30')
|
||||
assert self.order.payment_fee == prov.calculate_fee(self.order.total)
|
||||
assert self.order.payment_fee_tax_rate == Decimal('19.00')
|
||||
assert round_decimal(self.order.payment_fee * (1 - 100 / (100 + self.order.payment_fee_tax_rate))) == self.order.payment_fee_tax_value
|
||||
assert round_decimal(self.order.payment_fee * (
|
||||
1 - 100 / (100 + self.order.payment_fee_tax_rate))) == self.order.payment_fee_tax_value
|
||||
|
||||
def test_require_pending(self):
|
||||
self.order.status = Order.STATUS_PAID
|
||||
@@ -497,8 +594,37 @@ class OrderChangeManagerTests(TestCase):
|
||||
nop = self.order.positions.last()
|
||||
assert nop.item == self.shirt
|
||||
assert nop.price == self.shirt.default_price
|
||||
assert nop.tax_rate == self.shirt.tax_rate
|
||||
assert round_decimal(nop.price * (1 - 100 / (100 + self.shirt.tax_rate))) == nop.tax_value
|
||||
assert nop.tax_rate == self.shirt.tax_rule.rate
|
||||
assert round_decimal(nop.price * (1 - 100 / (100 + self.shirt.tax_rule.rate))) == nop.tax_value
|
||||
assert self.order.total == self.op1.price + self.op2.price + nop.price
|
||||
assert nop.positionid == 3
|
||||
|
||||
def test_add_item_net_price_success(self):
|
||||
self.tr19.price_includes_tax = False
|
||||
self.tr19.save()
|
||||
self.ocm.add_position(self.shirt, None, None, None)
|
||||
self.ocm.commit()
|
||||
self.order.refresh_from_db()
|
||||
assert self.order.positions.count() == 3
|
||||
nop = self.order.positions.last()
|
||||
assert nop.item == self.shirt
|
||||
assert nop.price == Decimal('14.28')
|
||||
assert nop.tax_rate == self.shirt.tax_rule.rate
|
||||
assert round_decimal(nop.price * (1 - 100 / (100 + self.shirt.tax_rule.rate))) == nop.tax_value
|
||||
assert self.order.total == self.op1.price + self.op2.price + nop.price
|
||||
assert nop.positionid == 3
|
||||
|
||||
def test_add_item_reverse_charge(self):
|
||||
self._enable_reverse_charge()
|
||||
self.ocm.add_position(self.shirt, None, None, None)
|
||||
self.ocm.commit()
|
||||
self.order.refresh_from_db()
|
||||
assert self.order.positions.count() == 3
|
||||
nop = self.order.positions.last()
|
||||
assert nop.item == self.shirt
|
||||
assert nop.price == Decimal('10.08')
|
||||
assert nop.tax_rate == Decimal('0.00')
|
||||
assert nop.tax_value == Decimal('0.00')
|
||||
assert self.order.total == self.op1.price + self.op2.price + nop.price
|
||||
assert nop.positionid == 3
|
||||
|
||||
@@ -510,8 +636,22 @@ class OrderChangeManagerTests(TestCase):
|
||||
nop = self.order.positions.last()
|
||||
assert nop.item == self.shirt
|
||||
assert nop.price == Decimal('13.00')
|
||||
assert nop.tax_rate == self.shirt.tax_rate
|
||||
assert round_decimal(nop.price * (1 - 100 / (100 + self.shirt.tax_rate))) == nop.tax_value
|
||||
assert nop.tax_rate == self.shirt.tax_rule.rate
|
||||
assert round_decimal(nop.price * (1 - 100 / (100 + self.shirt.tax_rule.rate))) == nop.tax_value
|
||||
assert self.order.total == self.op1.price + self.op2.price + nop.price
|
||||
|
||||
def test_add_item_custom_price_tax_always_included(self):
|
||||
self.tr19.price_includes_tax = False
|
||||
self.tr19.save()
|
||||
self.ocm.add_position(self.shirt, None, Decimal('11.90'), None)
|
||||
self.ocm.commit()
|
||||
self.order.refresh_from_db()
|
||||
assert self.order.positions.count() == 3
|
||||
nop = self.order.positions.last()
|
||||
assert nop.item == self.shirt
|
||||
assert nop.price == Decimal('11.90')
|
||||
assert nop.tax_rate == self.shirt.tax_rule.rate
|
||||
assert round_decimal(nop.price * (1 - 100 / (100 + self.shirt.tax_rule.rate))) == nop.tax_value
|
||||
assert self.order.total == self.op1.price + self.op2.price + nop.price
|
||||
|
||||
def test_add_item_quota_full(self):
|
||||
@@ -577,3 +717,44 @@ class OrderChangeManagerTests(TestCase):
|
||||
self.ocm.add_position(self.ticket, None, Decimal('0.00'))
|
||||
self.ocm.commit()
|
||||
assert self.order.invoices.count() == 1
|
||||
|
||||
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()
|
||||
|
||||
assert self.order.total == Decimal('46.30')
|
||||
assert self.order.payment_fee == prov.calculate_fee(self.order.total)
|
||||
assert self.order.payment_fee_tax_rate == Decimal('19.00')
|
||||
assert self.order.payment_fee_tax_value == Decimal('0.05')
|
||||
|
||||
ia = self._enable_reverse_charge()
|
||||
self.ocm.recalculate_taxes()
|
||||
self.ocm.commit()
|
||||
ops = list(self.order.positions.all())
|
||||
for op in ops:
|
||||
assert op.price == Decimal('21.50')
|
||||
assert op.tax_value == Decimal('0.00')
|
||||
assert op.tax_rate == Decimal('0.00')
|
||||
|
||||
assert self.order.total == Decimal('43.30')
|
||||
assert self.order.payment_fee == prov.calculate_fee(self.order.total)
|
||||
assert self.order.payment_fee_tax_rate == Decimal('0.00')
|
||||
assert self.order.payment_fee_tax_value == Decimal('0.00')
|
||||
|
||||
ia.vat_id_validated = False
|
||||
ia.save()
|
||||
|
||||
self.ocm.recalculate_taxes()
|
||||
self.ocm.commit()
|
||||
ops = list(self.order.positions.all())
|
||||
for op in ops:
|
||||
assert op.price == Decimal('23.01') # sic. we can't really avoid it.
|
||||
assert op.tax_value == Decimal('1.51')
|
||||
assert op.tax_rate == Decimal('7.00')
|
||||
|
||||
assert self.order.total == Decimal('46.32')
|
||||
assert self.order.payment_fee == prov.calculate_fee(self.order.total)
|
||||
assert self.order.payment_fee_tax_rate == Decimal('19.00')
|
||||
assert self.order.payment_fee_tax_value == Decimal('0.05')
|
||||
|
||||
@@ -108,7 +108,8 @@ def test_availability_date_cart_relative_subevents(event):
|
||||
event.date_from = now() + datetime.timedelta(days=5)
|
||||
event.has_subevents = True
|
||||
event.save()
|
||||
ticket = Item.objects.create(event=event, name='Early-bird ticket', tax_rate=Decimal('7.00'),
|
||||
tr7 = event.tax_rules.create(rate=Decimal('7.00'))
|
||||
ticket = Item.objects.create(event=event, name='Early-bird ticket', tax_rule=tr7,
|
||||
default_price=Decimal('23.00'), admission=True)
|
||||
|
||||
se1 = event.subevents.create(name="SE1", date_from=now() + datetime.timedelta(days=10))
|
||||
@@ -138,7 +139,8 @@ def test_availability_date_order_relative_subevents(event):
|
||||
event.date_from = now() + datetime.timedelta(days=5)
|
||||
event.has_subevents = True
|
||||
event.save()
|
||||
ticket = Item.objects.create(event=event, name='Early-bird ticket', tax_rate=Decimal('7.00'),
|
||||
tr7 = event.tax_rules.create(rate=Decimal('7.00'))
|
||||
ticket = Item.objects.create(event=event, name='Early-bird ticket', tax_rule=tr7,
|
||||
default_price=Decimal('23.00'), admission=True)
|
||||
|
||||
se1 = event.subevents.create(name="SE1", date_from=now() + datetime.timedelta(days=10))
|
||||
|
||||
@@ -2,8 +2,9 @@ from decimal import Decimal
|
||||
|
||||
import pytest
|
||||
from django.utils.timezone import now
|
||||
from django_countries.fields import Country
|
||||
|
||||
from pretix.base.models import Event, Organizer
|
||||
from pretix.base.models import Event, InvoiceAddress, Organizer
|
||||
from pretix.base.models.items import SubEventItem, SubEventItemVariation
|
||||
from pretix.base.services.pricing import get_price
|
||||
|
||||
@@ -42,143 +43,143 @@ def subevent(event):
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_base_item_default(item):
|
||||
assert get_price(item) == Decimal('23.00')
|
||||
assert get_price(item).gross == Decimal('23.00')
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_base_item_subevent_no_entry(item, subevent):
|
||||
assert get_price(item, subevent=subevent) == Decimal('23.00')
|
||||
assert get_price(item, subevent=subevent).gross == Decimal('23.00')
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_base_item_subevent_no_override(item, subevent):
|
||||
SubEventItem.objects.create(item=item, subevent=subevent, price=None)
|
||||
assert get_price(item, subevent=subevent) == Decimal('23.00')
|
||||
assert get_price(item, subevent=subevent).gross == Decimal('23.00')
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_base_item_subevent_override(item, subevent):
|
||||
SubEventItem.objects.create(item=item, subevent=subevent, price=Decimal('24.00'))
|
||||
assert get_price(item, subevent=subevent) == Decimal('24.00')
|
||||
assert get_price(item, subevent=subevent).gross == Decimal('24.00')
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_variation_with_default_item_price(item, variation):
|
||||
assert get_price(item, variation=variation) == Decimal('23.00')
|
||||
assert get_price(item, variation=variation).gross == Decimal('23.00')
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_variation_with_specific_price(item, variation):
|
||||
variation.default_price = Decimal('24.00')
|
||||
assert get_price(item, variation=variation) == Decimal('24.00')
|
||||
assert get_price(item, variation=variation).gross == Decimal('24.00')
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_variation_with_default_subevent_and_default_price(item, subevent, variation):
|
||||
SubEventItemVariation.objects.create(variation=variation, subevent=subevent, price=None)
|
||||
assert get_price(item, variation=variation, subevent=subevent) == Decimal('23.00')
|
||||
assert get_price(item, variation=variation, subevent=subevent).gross == Decimal('23.00')
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_variation_with_subevent_and_default_price(item, subevent, variation):
|
||||
SubEventItemVariation.objects.create(variation=variation, subevent=subevent, price=Decimal('24.00'))
|
||||
assert get_price(item, variation=variation, subevent=subevent) == Decimal('24.00')
|
||||
assert get_price(item, variation=variation, subevent=subevent).gross == Decimal('24.00')
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_variation_with_no_subevent_and_specific_price(item, subevent, variation):
|
||||
variation.default_price = Decimal('24.00')
|
||||
assert get_price(item, variation=variation, subevent=subevent) == Decimal('24.00')
|
||||
assert get_price(item, variation=variation, subevent=subevent).gross == Decimal('24.00')
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_variation_with_default_subevent_and_specific_price(item, subevent, variation):
|
||||
variation.default_price = Decimal('24.00')
|
||||
SubEventItemVariation.objects.create(variation=variation, subevent=subevent, price=None)
|
||||
assert get_price(item, variation=variation, subevent=subevent) == Decimal('24.00')
|
||||
assert get_price(item, variation=variation, subevent=subevent).gross == Decimal('24.00')
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_variation_with_subevent_and_specific_price(item, subevent, variation):
|
||||
variation.default_price = Decimal('24.00')
|
||||
SubEventItemVariation.objects.create(variation=variation, subevent=subevent, price=Decimal('26.00'))
|
||||
assert get_price(item, variation=variation, subevent=subevent) == Decimal('26.00')
|
||||
assert get_price(item, variation=variation, subevent=subevent).gross == Decimal('26.00')
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_voucher_no_override(item, subevent, voucher):
|
||||
assert get_price(item, subevent=subevent, voucher=voucher) == Decimal('23.00')
|
||||
assert get_price(item, subevent=subevent, voucher=voucher).gross == Decimal('23.00')
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_voucher_set_price(item, subevent, voucher):
|
||||
voucher.price_mode = 'set'
|
||||
voucher.value = Decimal('12.00')
|
||||
assert get_price(item, subevent=subevent, voucher=voucher) == Decimal('12.00')
|
||||
assert get_price(item, subevent=subevent, voucher=voucher).gross == Decimal('12.00')
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_voucher_subtract(item, subevent, voucher):
|
||||
voucher.price_mode = 'subtract'
|
||||
voucher.value = Decimal('12.00')
|
||||
assert get_price(item, subevent=subevent, voucher=voucher) == Decimal('11.00')
|
||||
assert get_price(item, subevent=subevent, voucher=voucher).gross == Decimal('11.00')
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_voucher_percent(item, subevent, voucher):
|
||||
voucher.price_mode = 'percent'
|
||||
voucher.value = Decimal('10.00')
|
||||
assert get_price(item, subevent=subevent, voucher=voucher) == Decimal('20.70')
|
||||
assert get_price(item, subevent=subevent, voucher=voucher).gross == Decimal('20.70')
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_free_price_ignored_if_disabled(item):
|
||||
assert get_price(item, custom_price=Decimal('42.00')) == Decimal('23.00')
|
||||
assert get_price(item, custom_price=Decimal('42.00')).gross == Decimal('23.00')
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_free_price_ignored_if_lower(item):
|
||||
item.free_price = True
|
||||
assert get_price(item, custom_price=Decimal('12.00')) == Decimal('23.00')
|
||||
assert get_price(item, custom_price=Decimal('12.00')).gross == Decimal('23.00')
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_free_price_ignored_if_lower_than_voucher(item, voucher):
|
||||
voucher.price_mode = 'set'
|
||||
voucher.value = Decimal('50.00')
|
||||
assert get_price(item, voucher=voucher, custom_price=Decimal('40.00')) == Decimal('50.00')
|
||||
assert get_price(item, voucher=voucher, custom_price=Decimal('40.00')).gross == Decimal('50.00')
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_free_price_ignored_if_lower_than_subevent(item, subevent):
|
||||
item.free_price = True
|
||||
SubEventItem.objects.create(item=item, subevent=subevent, price=Decimal('50.00'))
|
||||
assert get_price(item, subevent=subevent, custom_price=Decimal('40.00')) == Decimal('50.00')
|
||||
assert get_price(item, subevent=subevent, custom_price=Decimal('40.00')).gross == Decimal('50.00')
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_free_price_ignored_if_lower_than_variation(item, variation):
|
||||
variation.default_price = Decimal('50.00')
|
||||
item.free_price = True
|
||||
assert get_price(item, variation=variation, custom_price=Decimal('40.00')) == Decimal('50.00')
|
||||
assert get_price(item, variation=variation, custom_price=Decimal('40.00')).gross == Decimal('50.00')
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_free_price_accepted(item):
|
||||
item.free_price = True
|
||||
assert get_price(item, custom_price=Decimal('42.00')) == Decimal('42.00')
|
||||
assert get_price(item, custom_price=Decimal('42.00')).gross == Decimal('42.00')
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_free_price_string(item):
|
||||
item.free_price = True
|
||||
assert get_price(item, custom_price='42,00') == Decimal('42.00')
|
||||
assert get_price(item, custom_price='42,00').gross == Decimal('42.00')
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_free_price_float(item):
|
||||
item.free_price = True
|
||||
assert get_price(item, custom_price=42.00) == Decimal('42.00')
|
||||
assert get_price(item, custom_price=42.00).gross == Decimal('42.00')
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@@ -191,5 +192,136 @@ def test_free_price_limit(item):
|
||||
@pytest.mark.django_db
|
||||
def test_free_price_net(item):
|
||||
item.free_price = True
|
||||
item.tax_rate = 19
|
||||
assert get_price(item, custom_price=Decimal('100.00'), custom_price_is_net=True) == Decimal('119.00')
|
||||
item.tax_rule = item.event.tax_rules.create(rate=Decimal('19.00'))
|
||||
assert get_price(item, custom_price=Decimal('100.00'), custom_price_is_net=True).gross == Decimal('119.00')
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_tax_included(item):
|
||||
item.default_price = Decimal('119.00')
|
||||
item.tax_rule = item.event.tax_rules.create(rate=Decimal('19.00'), price_includes_tax=True)
|
||||
assert get_price(item).gross == Decimal('119.00')
|
||||
assert get_price(item).net == Decimal('100.00')
|
||||
assert get_price(item).tax == Decimal('19.00')
|
||||
assert get_price(item).rate == Decimal('19.00')
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_tax_none(item):
|
||||
item.default_price = Decimal('100.00')
|
||||
assert get_price(item).gross == Decimal('100.00')
|
||||
assert get_price(item).net == Decimal('100.00')
|
||||
assert get_price(item).tax == Decimal('0.00')
|
||||
assert get_price(item).rate == Decimal('0.00')
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_tax_added(item):
|
||||
item.default_price = Decimal('100.00')
|
||||
item.tax_rule = item.event.tax_rules.create(rate=Decimal('19.00'), price_includes_tax=False)
|
||||
assert get_price(item).gross == Decimal('119.00')
|
||||
assert get_price(item).net == Decimal('100.00')
|
||||
assert get_price(item).tax == Decimal('19.00')
|
||||
assert get_price(item).rate == Decimal('19.00')
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_tax_reverse_charge_valid(item):
|
||||
item.default_price = Decimal('100.00')
|
||||
item.tax_rule = item.event.tax_rules.create(
|
||||
rate=Decimal('19.00'), price_includes_tax=False,
|
||||
eu_reverse_charge=True, home_country=Country('DE')
|
||||
)
|
||||
ia = InvoiceAddress(
|
||||
is_business=True, vat_id="EU1234", vat_id_validated=True,
|
||||
country=Country('BE')
|
||||
)
|
||||
assert item.tax_rule.is_reverse_charge(ia)
|
||||
assert get_price(item, invoice_address=ia).gross == Decimal('100.00')
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_tax_reverse_charge_disabled(item):
|
||||
item.default_price = Decimal('100.00')
|
||||
item.tax_rule = item.event.tax_rules.create(
|
||||
rate=Decimal('19.00'), price_includes_tax=False,
|
||||
eu_reverse_charge=False, home_country=Country('DE')
|
||||
)
|
||||
ia = InvoiceAddress(
|
||||
is_business=True, vat_id="EU1234", vat_id_validated=True,
|
||||
country=Country('BE')
|
||||
)
|
||||
assert not item.tax_rule.is_reverse_charge(ia)
|
||||
assert get_price(item, invoice_address=ia).gross == Decimal('119.00')
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_tax_reverse_charge_no_country(item):
|
||||
item.default_price = Decimal('100.00')
|
||||
item.tax_rule = item.event.tax_rules.create(
|
||||
rate=Decimal('19.00'), price_includes_tax=False,
|
||||
eu_reverse_charge=True, home_country=Country('DE')
|
||||
)
|
||||
ia = InvoiceAddress(
|
||||
is_business=True, vat_id="EU1234", vat_id_validated=True,
|
||||
)
|
||||
assert not item.tax_rule.is_reverse_charge(ia)
|
||||
assert get_price(item, invoice_address=ia).gross == Decimal('119.00')
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_tax_reverse_charge_non_eu_country(item):
|
||||
item.default_price = Decimal('100.00')
|
||||
item.tax_rule = item.event.tax_rules.create(
|
||||
rate=Decimal('19.00'), price_includes_tax=False,
|
||||
eu_reverse_charge=True, home_country=Country('DE')
|
||||
)
|
||||
ia = InvoiceAddress(
|
||||
country=Country('US')
|
||||
)
|
||||
assert not item.tax_rule.is_reverse_charge(ia)
|
||||
assert get_price(item, invoice_address=ia).gross == Decimal('100.00')
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_tax_reverse_charge_same_country(item):
|
||||
item.default_price = Decimal('100.00')
|
||||
item.tax_rule = item.event.tax_rules.create(
|
||||
rate=Decimal('19.00'), price_includes_tax=False,
|
||||
eu_reverse_charge=True, home_country=Country('DE')
|
||||
)
|
||||
ia = InvoiceAddress(
|
||||
is_business=True, vat_id="EU1234", vat_id_validated=True,
|
||||
country=Country('DE')
|
||||
)
|
||||
assert not item.tax_rule.is_reverse_charge(ia)
|
||||
assert get_price(item, invoice_address=ia).gross == Decimal('119.00')
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_tax_reverse_charge_consumer(item):
|
||||
item.default_price = Decimal('100.00')
|
||||
item.tax_rule = item.event.tax_rules.create(
|
||||
rate=Decimal('19.00'), price_includes_tax=False,
|
||||
eu_reverse_charge=True, home_country=Country('DE')
|
||||
)
|
||||
ia = InvoiceAddress(
|
||||
is_business=False, country=Country('BE')
|
||||
)
|
||||
assert not item.tax_rule.is_reverse_charge(ia)
|
||||
assert get_price(item, invoice_address=ia).gross == Decimal('119.00')
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_tax_reverse_charge_invalid_vat_id(item):
|
||||
item.default_price = Decimal('100.00')
|
||||
item.tax_rule = item.event.tax_rules.create(
|
||||
rate=Decimal('19.00'), price_includes_tax=False,
|
||||
eu_reverse_charge=True, home_country=Country('DE')
|
||||
)
|
||||
ia = InvoiceAddress(
|
||||
is_business=True, vat_id="EU1234", vat_id_validated=False,
|
||||
country=Country('BE')
|
||||
)
|
||||
assert not item.tax_rule.is_reverse_charge(ia)
|
||||
assert get_price(item, invoice_address=ia).gross == Decimal('119.00')
|
||||
|
||||
Reference in New Issue
Block a user