Add sub-events and relative date settings (#503)

* Data model

* little crud

* SubEventItemForm etc

* Drop SubEventItem.active, quota editor

* Fix failing tests

* First frontend stuff

* Addons form stuff

* Quota calculation

* net price display on EventIndex

* Add tests, solve some bugs

* Correct quota selection in more places, consolidate pricing logic

* Fix failing quota tests

* Fix TypeError

* Add tests for checkout

* Fixed a bug in QuotaForm

* Prevent immutable cart if a quota was removed from an item

* Add tests for pricing

* Handle waiting list

* Filter in check-in list

* Fixed import lost in rebase

* Fix waiting list widget

* Voucher management

* Voucher redemption

* Fix broken tests

* Add subevents to OrderChangeManager

* Create a subevent during event creation

* Fix bulk voucher creation

* Introduce subevent.active

* Copy from for subevents

* Show active in list

* ICal download for subevents

* Check start and end of presale

* Failing tests / show cart logic

* Test

* Rebase migrations

* REST API integration of sub-events

* Integrate quota calculation into the traditional quota form

* Make subevent argument to add_position optional

* Log-display foo

* pretixdroid and subevents

* Filter by subevent

* Add more tests

* Some mor tests

* Rebase fixes

* More tests

* Relative dates

* Restrict selection in relative datetime widgets

* Filter subevent list

* Re-label has_subevents

* Rebase fixes, subevents in calendar view

* Performance and caching issues

* Refactor calendar templates

* Permission tests

* Calendar fixes and month selection

* subevent selection

* Rename subevents to dates

* Add tests for calendar views
This commit is contained in:
Raphael Michel
2017-07-11 13:56:00 +02:00
committed by GitHub
parent 554800c06f
commit 8123effa65
141 changed files with 5920 additions and 1012 deletions

View File

@@ -1,9 +1,10 @@
import datetime
import sys
from datetime import timedelta
from datetime import date, timedelta
from decimal import Decimal
import pytest
import pytz
from django.conf import settings
from django.core.exceptions import ValidationError
from django.core.files.storage import default_storage
@@ -15,6 +16,9 @@ from pretix.base.models import (
CachedFile, CartPosition, Event, Item, ItemCategory, ItemVariation, Order,
OrderPosition, Organizer, Question, Quota, User, Voucher, WaitingListEntry,
)
from pretix.base.models.event import SubEvent
from pretix.base.models.items import SubEventItem, SubEventItemVariation
from pretix.base.reldate import RelativeDate, RelativeDateWrapper
from pretix.base.services.orders import (
OrderError, cancel_order, mark_order_paid, perform_order,
)
@@ -378,6 +382,64 @@ class QuotaTestCase(BaseQuotaTestCase):
with self.assertNumQueries(1):
self.assertEqual(self.var1.check_quotas(_cache=cache, count_waitinglist=False), (Quota.AVAILABILITY_OK, 1))
def test_subevent_isolation(self):
self.event.has_subevents = True
self.event.save()
se1 = self.event.subevents.create(date_from=now(), name="SE 1")
se2 = self.event.subevents.create(date_from=now(), name="SE 2")
q1 = self.event.quotas.create(name="Q1", subevent=se1, size=50)
q2 = self.event.quotas.create(name="Q2", subevent=se2, size=50)
q1.items.add(self.item1)
q2.items.add(self.item1)
# Create orders
order = Order.objects.create(event=self.event, status=Order.STATUS_PAID,
expires=now() + timedelta(days=3),
total=6)
OrderPosition.objects.create(order=order, item=self.item1, subevent=se1, price=2)
OrderPosition.objects.create(order=order, item=self.item1, subevent=se1, price=2)
OrderPosition.objects.create(order=order, item=self.item1, subevent=se2, price=2)
order = Order.objects.create(event=self.event, status=Order.STATUS_PENDING,
expires=now() + timedelta(days=3),
total=8)
OrderPosition.objects.create(order=order, item=self.item1, subevent=se1, price=2)
OrderPosition.objects.create(order=order, item=self.item1, subevent=se1, price=2)
OrderPosition.objects.create(order=order, item=self.item1, subevent=se1, price=2)
OrderPosition.objects.create(order=order, item=self.item1, subevent=se2, price=2)
Voucher.objects.create(item=self.item1, event=self.event, valid_until=now() + timedelta(days=5),
block_quota=True, max_usages=6, subevent=se1)
Voucher.objects.create(item=self.item1, event=self.event, valid_until=now() + timedelta(days=5),
block_quota=True, max_usages=4, subevent=se2)
for i in range(8):
CartPosition.objects.create(event=self.event, item=self.item1, price=2, subevent=se1,
expires=now() + timedelta(days=3))
for i in range(5):
CartPosition.objects.create(event=self.event, item=self.item1, price=2, subevent=se2,
expires=now() + timedelta(days=3))
for i in range(16):
WaitingListEntry.objects.create(
event=self.event, item=self.item1, email='foo@bar.com', subevent=se1
)
for i in range(13):
WaitingListEntry.objects.create(
event=self.event, item=self.item1, email='foo@bar.com', subevent=se2
)
with self.assertRaises(TypeError):
self.item1.check_quotas()
self.assertEqual(self.item1.check_quotas(subevent=se1), (Quota.AVAILABILITY_OK, 50 - 5 - 6 - 8 - 16))
self.assertEqual(self.item1.check_quotas(subevent=se2), (Quota.AVAILABILITY_OK, 50 - 2 - 4 - 5 - 13))
self.assertEqual(q1.availability(), (Quota.AVAILABILITY_OK, 50 - 5 - 6 - 8 - 16))
self.assertEqual(q2.availability(), (Quota.AVAILABILITY_OK, 50 - 2 - 4 - 5 - 13))
self.event.has_subevents = False
self.event.save()
class WaitingListTestCase(BaseQuotaTestCase):
@@ -496,27 +558,27 @@ class VoucherTestCase(BaseQuotaTestCase):
def test_calculate_price_none(self):
v = Voucher.objects.create(event=self.event, price_mode='none', value=Decimal('10.00'))
v.calculate_price(Decimal('23.42')) == Decimal('23.42')
assert v.calculate_price(Decimal('23.42')) == Decimal('23.42')
def test_calculate_price_set_empty(self):
v = Voucher.objects.create(event=self.event, price_mode='set')
v.calculate_price(Decimal('23.42')) == Decimal('23.42')
assert v.calculate_price(Decimal('23.42')) == Decimal('23.42')
def test_calculate_price_set(self):
v = Voucher.objects.create(event=self.event, price_mode='set', value=Decimal('10.00'))
v.calculate_price(Decimal('23.42')) == Decimal('10.00')
assert v.calculate_price(Decimal('23.42')) == Decimal('10.00')
def test_calculate_price_set_zero(self):
v = Voucher.objects.create(event=self.event, price_mode='set', value=Decimal('0.00'))
v.calculate_price(Decimal('23.42')) == Decimal('0.00')
assert v.calculate_price(Decimal('23.42')) == Decimal('0.00')
def test_calculate_price_subtract(self):
v = Voucher.objects.create(event=self.event, price_mode='subtract', value=Decimal('10.00'))
v.calculate_price(Decimal('23.42')) == Decimal('13.42')
assert v.calculate_price(Decimal('23.42')) == Decimal('13.42')
def test_calculate_price_percent(self):
v = Voucher.objects.create(event=self.event, price_mode='percent', value=Decimal('23.00'))
v.calculate_price(Decimal('100.00')) == Decimal('77.00')
assert v.calculate_price(Decimal('100.00')) == Decimal('77.00')
class OrderTestCase(BaseQuotaTestCase):
@@ -529,10 +591,10 @@ class OrderTestCase(BaseQuotaTestCase):
expires=now() + timedelta(days=5), total=46
)
self.quota.items.add(self.item1)
OrderPosition.objects.create(order=self.order, item=self.item1,
variation=None, price=23)
OrderPosition.objects.create(order=self.order, item=self.item1,
variation=None, price=23)
self.op1 = OrderPosition.objects.create(order=self.order, item=self.item1,
variation=None, price=23)
self.op2 = OrderPosition.objects.create(order=self.order, item=self.item1,
variation=None, price=23)
def test_paid_in_time(self):
self.quota.size = 0
@@ -560,6 +622,29 @@ class OrderTestCase(BaseQuotaTestCase):
self.order = Order.objects.get(id=self.order.id)
self.assertEqual(self.order.status, Order.STATUS_EXPIRED)
def test_paid_expired_after_last_date_subevent_relative(self):
self.event.has_subevents = True
self.event.save()
se1 = self.event.subevents.create(name="SE1", date_from=now() + timedelta(days=10))
se2 = self.event.subevents.create(name="SE2", date_from=now() + timedelta(days=1))
self.op1.subevent = se1
self.op1.save()
self.op2.subevent = se2
self.op2.save()
self.event.settings.set('payment_term_last', RelativeDateWrapper(
RelativeDate(days_before=2, time=None, base_date_name='date_from')
))
self.order.status = Order.STATUS_EXPIRED
self.order.expires = now() - timedelta(days=2)
self.order.save()
with self.assertRaises(Quota.QuotaExceededException):
mark_order_paid(self.order)
self.order = Order.objects.get(id=self.order.id)
self.assertEqual(self.order.status, Order.STATUS_EXPIRED)
self.event.has_subevents = False
self.event.save()
def test_paid_expired_late_not_allowed(self):
self.event.settings.payment_term_accept_late = False
self.order.status = Order.STATUS_EXPIRED
@@ -615,6 +700,86 @@ class OrderTestCase(BaseQuotaTestCase):
self.event.settings.set('last_order_modification_date', now() - timedelta(days=1))
assert not self.order.can_modify_answers
def test_can_modify_answers_subevent(self):
self.event.has_subevents = True
self.event.save()
se1 = self.event.subevents.create(name="SE1", date_from=now() + timedelta(days=10))
se2 = self.event.subevents.create(name="SE2", date_from=now() + timedelta(days=8))
se3 = self.event.subevents.create(name="SE2", date_from=now() + timedelta(days=1))
self.op1.subevent = se1
self.op1.save()
self.op2.subevent = se2
self.op2.save()
self.event.settings.set('last_order_modification_date', RelativeDateWrapper(
RelativeDate(days_before=2, time=None, base_date_name='date_from')
))
assert self.order.can_modify_answers
self.op2.subevent = se3
self.op2.save()
assert not self.order.can_modify_answers
self.event.has_subevents = False
self.event.save()
def test_payment_term_last_relative(self):
self.event.settings.set('payment_term_last', date(2017, 5, 3))
assert self.order.payment_term_last == datetime.datetime(2017, 5, 3, 23, 59, 59, tzinfo=pytz.UTC)
self.event.date_from = datetime.datetime(2017, 5, 3, 12, 0, 0, tzinfo=pytz.UTC)
self.event.save()
self.event.settings.set('payment_term_last', RelativeDateWrapper(
RelativeDate(days_before=2, time=None, base_date_name='date_from')
))
assert self.order.payment_term_last == datetime.datetime(2017, 5, 1, 23, 59, 59, tzinfo=pytz.UTC)
def test_payment_term_last_subevent(self):
self.event.has_subevents = True
self.event.save()
se1 = self.event.subevents.create(name="SE1", date_from=now() + timedelta(days=10))
se2 = self.event.subevents.create(name="SE2", date_from=now() + timedelta(days=8))
se3 = self.event.subevents.create(name="SE2", date_from=now() + timedelta(days=1))
self.op1.subevent = se1
self.op1.save()
self.op2.subevent = se2
self.op2.save()
self.event.settings.set('payment_term_last', RelativeDateWrapper(
RelativeDate(days_before=2, time=None, base_date_name='date_from')
))
assert self.order.payment_term_last > now()
self.op2.subevent = se3
self.op2.save()
assert self.order.payment_term_last < now()
self.event.has_subevents = False
self.event.save()
def test_ticket_download_date_relative(self):
self.event.settings.set('ticket_download_date', datetime.datetime(2017, 5, 3, 12, 59, 59, tzinfo=pytz.UTC))
assert self.order.ticket_download_date == datetime.datetime(2017, 5, 3, 12, 59, 59, tzinfo=pytz.UTC)
self.event.date_from = datetime.datetime(2017, 5, 3, 12, 0, 0, tzinfo=pytz.UTC)
self.event.save()
self.event.settings.set('ticket_download_date', RelativeDateWrapper(
RelativeDate(days_before=2, time=None, base_date_name='date_from')
))
assert self.order.ticket_download_date == datetime.datetime(2017, 5, 1, 12, 0, 0, tzinfo=pytz.UTC)
def test_ticket_download_date_subevent(self):
self.event.has_subevents = True
self.event.save()
se1 = self.event.subevents.create(name="SE1", date_from=now() + timedelta(days=10))
se2 = self.event.subevents.create(name="SE2", date_from=now() + timedelta(days=8))
se3 = self.event.subevents.create(name="SE2", date_from=now() + timedelta(days=1))
self.op1.subevent = se1
self.op1.save()
self.op2.subevent = se2
self.op2.save()
self.event.settings.set('ticket_download_date', RelativeDateWrapper(
RelativeDate(days_before=2, time=None, base_date_name='date_from')
))
assert self.order.ticket_download_date > now()
self.op2.subevent = se3
self.op2.save()
assert self.order.ticket_download_date < now()
self.event.has_subevents = False
self.event.save()
def test_can_cancel_order(self):
item1 = Item.objects.create(event=self.event, name="Ticket", default_price=23,
admission=True, allow_cancel=True)
@@ -771,6 +936,41 @@ class EventTest(TestCase):
self.assertIn('slug', str(context.exception))
class SubEventTest(TestCase):
@classmethod
def setUpTestData(cls):
cls.organizer = Organizer.objects.create(name='Dummy', slug='dummy')
cls.event = Event.objects.create(
organizer=cls.organizer, name='Dummy', slug='dummy',
date_from=now(), date_to=now() - timedelta(hours=1),
has_subevents=True
)
cls.se = SubEvent.objects.create(
name='Testsub', date_from=now(), event=cls.event
)
def test_override_prices(self):
i = Item.objects.create(
event=self.event, name="Ticket", default_price=23,
active=True, available_until=now() + timedelta(days=1),
)
SubEventItem.objects.create(item=i, subevent=self.se, price=Decimal('30.00'))
assert self.se.item_price_overrides == {
i.pk: Decimal('30.00')
}
def test_override_var_prices(self):
i = Item.objects.create(
event=self.event, name="Ticket", default_price=23,
active=True, available_until=now() + timedelta(days=1),
)
v = i.variations.create(value='Type 1')
SubEventItemVariation.objects.create(variation=v, subevent=self.se, price=Decimal('30.00'))
assert self.se.var_price_overrides == {
v.pk: Decimal('30.00')
}
class CachedFileTestCase(TestCase):
def test_file_handling(self):
cf = CachedFile()

View File

@@ -7,8 +7,12 @@ from django.test import TestCase
from django.utils.timezone import make_aware, now
from pretix.base.decimal import round_decimal
from pretix.base.models import Event, Item, Order, OrderPosition, Organizer
from pretix.base.models import (
CartPosition, Event, Item, Order, OrderPosition, Organizer,
)
from pretix.base.models.items import SubEventItem
from pretix.base.payment import FreeOrderProvider
from pretix.base.reldate import RelativeDate, RelativeDateWrapper
from pretix.base.services.orders import (
OrderChangeManager, OrderError, _create_order, expire_orders,
)
@@ -71,6 +75,52 @@ def test_expiry_last(event):
assert (order.expires - today).days == 5
@pytest.mark.django_db
def test_expiry_last_relative(event):
today = now()
event.settings.set('payment_term_days', 5)
event.settings.set('payment_term_weekdays', False)
event.date_from = now() + timedelta(days=5)
event.save()
event.settings.set('payment_term_last', RelativeDateWrapper(
RelativeDate(days_before=2, time=None, base_date_name='date_from')
))
order = _create_order(event, email='dummy@example.org', positions=[],
now_dt=today, payment_provider=FreeOrderProvider(event),
locale='de')
assert (order.expires - today).days == 3
@pytest.mark.django_db
def test_expiry_last_relative_subevents(event):
today = now()
event.settings.set('payment_term_days', 100)
event.settings.set('payment_term_weekdays', False)
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'),
default_price=Decimal('23.00'), admission=True)
se1 = event.subevents.create(name="SE1", date_from=now() + timedelta(days=10))
se2 = event.subevents.create(name="SE2", date_from=now() + timedelta(days=8))
cp1 = CartPosition.objects.create(
item=ticket, price=23, expires=now() + timedelta(days=1), subevent=se1, event=event, cart_id="123"
)
cp2 = CartPosition.objects.create(
item=ticket, price=23, expires=now() + timedelta(days=1), subevent=se2, event=event, cart_id="123"
)
event.settings.set('payment_term_last', RelativeDateWrapper(
RelativeDate(days_before=2, time=None, base_date_name='date_from')
))
order = _create_order(event, email='dummy@example.org', positions=[cp1, cp2],
now_dt=today, payment_provider=FreeOrderProvider(event),
locale='de')
assert (order.expires - today).days == 6
@pytest.mark.django_db
def test_expiry_dst(event):
event.settings.set('timezone', 'Europe/Berlin')
@@ -153,6 +203,63 @@ class OrderChangeManagerTests(TestCase):
price=Decimal("23.00"), attendee_name="Dieter", positionid=2
)
self.ocm = OrderChangeManager(self.order, None)
self.quota = self.event.quotas.create(name='Test', size=None)
self.quota.items.add(self.ticket)
self.quota.items.add(self.ticket2)
self.quota.items.add(self.shirt)
def test_change_subevent_quota_required(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.op1.subevent = se1
self.op1.save()
self.quota.subevent = se1
self.quota.save()
with self.assertRaises(OrderError):
self.ocm.change_subevent(self.op1, se2)
def test_change_subevent_success(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())
SubEventItem.objects.create(subevent=se2, item=self.ticket, price=12)
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 == 12
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()
se1 = self.event.subevents.create(name="Foo", date_from=now())
se2 = self.event.subevents.create(name="Bar", date_from=now())
self.op1.subevent = se1
self.op1.save()
self.quota.subevent = se2
self.quota.size = 0
self.quota.save()
self.ocm.change_subevent(self.op1, se2)
with self.assertRaises(OrderError):
self.ocm.commit()
self.op1.refresh_from_db()
assert self.op1.subevent == se1
def test_change_item_quota_required(self):
self.quota.delete()
with self.assertRaises(OrderError):
self.ocm.change_item(self.op1, self.shirt, None)
def test_change_item_success(self):
self.ocm.change_item(self.op1, self.shirt, None)
@@ -325,6 +432,11 @@ class OrderChangeManagerTests(TestCase):
assert self.order.total == 46
assert self.order.status == Order.STATUS_PAID
def test_add_item_quota_required(self):
self.quota.delete()
with self.assertRaises(OrderError):
self.ocm.add_position(self.shirt, None, None, None)
def test_add_item_success(self):
self.ocm.add_position(self.shirt, None, None, None)
self.ocm.commit()
@@ -375,3 +487,26 @@ class OrderChangeManagerTests(TestCase):
self.shirt.category = self.event.categories.create(name='Add-ons', is_addon=True)
with self.assertRaises(OrderError):
self.ocm.add_position(self.shirt, None, Decimal('13.00'), None)
def test_add_item_subevent_required(self):
self.event.has_subevents = True
self.event.save()
with self.assertRaises(OrderError):
self.ocm.add_position(self.ticket, None, None, None)
def test_add_item_subevent_price(self):
self.event.has_subevents = True
self.event.save()
se1 = self.event.subevents.create(name="Foo", date_from=now())
SubEventItem.objects.create(subevent=se1, item=self.ticket, price=12)
self.quota.subevent = se1
self.quota.save()
self.ocm.add_position(self.ticket, None, None, subevent=se1)
self.ocm.commit()
self.order.refresh_from_db()
assert self.order.positions.count() == 3
nop = self.order.positions.last()
assert nop.item == self.ticket
assert nop.price == Decimal('12.00')
assert nop.subevent == se1

View File

@@ -6,7 +6,10 @@ import pytz
from django.utils.timezone import now
from tests.testdummy.payment import DummyPaymentProvider
from pretix.base.models import Event, Organizer
from pretix.base.models import (
CartPosition, Event, Item, Order, OrderPosition, Organizer,
)
from pretix.base.reldate import RelativeDate, RelativeDateWrapper
@pytest.fixture
@@ -70,6 +73,23 @@ def test_availability_date_not_available(event):
assert not result
@pytest.mark.django_db
def test_availability_date_relative(event):
event.settings.set('timezone', 'US/Pacific')
tz = pytz.timezone('US/Pacific')
event.date_from = tz.localize(datetime.datetime(2016, 12, 3, 12, 0, 0))
event.save()
prov = DummyPaymentProvider(event)
prov.settings.set('_availability_date', RelativeDateWrapper(
RelativeDate(days_before=2, time=None, base_date_name='date_from')
))
utc = pytz.timezone('UTC')
assert prov._is_still_available(tz.localize(datetime.datetime(2016, 11, 30, 23, 0, 0)).astimezone(utc))
assert prov._is_still_available(tz.localize(datetime.datetime(2016, 12, 1, 23, 59, 0)).astimezone(utc))
assert not prov._is_still_available(tz.localize(datetime.datetime(2016, 12, 2, 0, 0, 1)).astimezone(utc))
@pytest.mark.django_db
def test_availability_date_timezones(event):
event.settings.set('timezone', 'US/Pacific')
@@ -81,3 +101,71 @@ def test_availability_date_timezones(event):
assert prov._is_still_available(tz.localize(datetime.datetime(2016, 11, 30, 23, 0, 0)).astimezone(utc))
assert prov._is_still_available(tz.localize(datetime.datetime(2016, 12, 1, 23, 59, 0)).astimezone(utc))
assert not prov._is_still_available(tz.localize(datetime.datetime(2016, 12, 2, 0, 0, 1)).astimezone(utc))
@pytest.mark.django_db
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'),
default_price=Decimal('23.00'), admission=True)
se1 = event.subevents.create(name="SE1", date_from=now() + datetime.timedelta(days=10))
se2 = event.subevents.create(name="SE2", date_from=now() + datetime.timedelta(days=3))
CartPosition.objects.create(
item=ticket, price=23, expires=now() + datetime.timedelta(days=1), subevent=se1, event=event, cart_id="123"
)
CartPosition.objects.create(
item=ticket, price=23, expires=now() + datetime.timedelta(days=1), subevent=se2, event=event, cart_id="123"
)
prov = DummyPaymentProvider(event)
prov.settings.set('_availability_date', RelativeDateWrapper(
RelativeDate(days_before=3, time=None, base_date_name='date_from')
))
assert prov._is_still_available(cart_id="123")
prov.settings.set('_availability_date', RelativeDateWrapper(
RelativeDate(days_before=4, time=None, base_date_name='date_from')
))
assert not prov._is_still_available(cart_id="123")
@pytest.mark.django_db
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'),
default_price=Decimal('23.00'), admission=True)
se1 = event.subevents.create(name="SE1", date_from=now() + datetime.timedelta(days=10))
se2 = event.subevents.create(name="SE2", date_from=now() + datetime.timedelta(days=3))
order = Order.objects.create(
code='FOO', event=event, email='dummy@dummy.test',
status=Order.STATUS_PENDING,
datetime=now(), expires=now() + datetime.timedelta(days=10),
total=Decimal('46.00'), payment_provider='dummtest'
)
OrderPosition.objects.create(
order=order, item=ticket, variation=None, subevent=se1,
price=Decimal("23.00"), attendee_name="Peter", positionid=1
)
OrderPosition.objects.create(
order=order, item=ticket, variation=None, subevent=se2,
price=Decimal("23.00"), attendee_name="Dieter", positionid=2
)
prov = DummyPaymentProvider(event)
prov.settings.set('_availability_date', RelativeDateWrapper(
RelativeDate(days_before=3, time=None, base_date_name='date_from')
))
assert prov._is_still_available(order=order)
prov.settings.set('_availability_date', RelativeDateWrapper(
RelativeDate(days_before=4, time=None, base_date_name='date_from')
))
assert not prov._is_still_available(order=order)

View File

@@ -0,0 +1,195 @@
from decimal import Decimal
import pytest
from django.utils.timezone import now
from pretix.base.models import Event, Organizer
from pretix.base.models.items import SubEventItem, SubEventItemVariation
from pretix.base.services.pricing import get_price
@pytest.fixture
def event():
o = Organizer.objects.create(name='Dummy', slug='dummy')
event = Event.objects.create(
organizer=o, name='Dummy', slug='dummy',
date_from=now()
)
return event
@pytest.fixture
def item(event):
return event.items.create(name='Ticket', default_price=Decimal('23.00'))
@pytest.fixture
def variation(item):
return item.variations.create(value='Premium', default_price=None)
@pytest.fixture
def voucher(event):
return event.vouchers.create()
@pytest.fixture
def subevent(event):
event.has_subevents = True
event.save()
return event.subevents.create(name='Foobar', date_from=now())
@pytest.mark.django_db
def test_base_item_default(item):
assert get_price(item) == 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')
@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')
@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')
@pytest.mark.django_db
def test_variation_with_default_item_price(item, variation):
assert get_price(item, variation=variation) == 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')
@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')
@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')
@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')
@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')
@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')
@pytest.mark.django_db
def test_voucher_no_override(item, subevent, voucher):
assert get_price(item, subevent=subevent, voucher=voucher) == 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')
@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')
@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')
@pytest.mark.django_db
def test_free_price_ignored_if_disabled(item):
assert get_price(item, custom_price=Decimal('42.00')) == 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')
@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')
@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')
@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')
@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')
@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')
@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')
@pytest.mark.django_db
def test_free_price_limit(item):
item.free_price = True
with pytest.raises(ValueError):
get_price(item, custom_price=Decimal('200000000'))
@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')

View File

@@ -0,0 +1,82 @@
from datetime import datetime, time
import pytest
import pytz
from pretix.base.models import Event, Organizer
from pretix.base.reldate import RelativeDate, RelativeDateWrapper
TOKYO = pytz.timezone('Asia/Tokyo')
@pytest.fixture
def event():
o = Organizer.objects.create(name='Dummy', slug='dummy')
event = Event.objects.create(
organizer=o, name='Dummy', slug='dummy',
date_from=datetime(2017, 12, 27, 5, 0, 0, tzinfo=TOKYO),
presale_start=datetime(2017, 12, 1, 5, 0, 0, tzinfo=TOKYO),
plugins='pretix.plugins.banktransfer'
)
event.settings.timezone = "Asia/Tokyo"
return event
@pytest.mark.django_db
def test_absolute_date(event):
d = datetime(2017, 12, 25, 5, 0, 0, tzinfo=TOKYO)
rdw = RelativeDateWrapper(d)
assert rdw.datetime(event) == d
assert rdw.to_string() == d.isoformat()
@pytest.mark.django_db
def test_relative_date_without_time(event):
rdw = RelativeDateWrapper(RelativeDate(days_before=1, time=None, base_date_name='date_from'))
assert rdw.datetime(event).astimezone(TOKYO) == datetime(2017, 12, 26, 5, 0, 0, tzinfo=TOKYO)
assert rdw.to_string() == 'RELDATE/1/-/date_from/'
@pytest.mark.django_db
def test_relative_date_other_base_point(event):
rdw = RelativeDateWrapper(RelativeDate(days_before=1, time=None, base_date_name='presale_start'))
assert rdw.datetime(event) == datetime(2017, 11, 30, 5, 0, 0, tzinfo=TOKYO)
assert rdw.to_string() == 'RELDATE/1/-/presale_start/'
# presale_end is unset, defaults to date_from
rdw = RelativeDateWrapper(RelativeDate(days_before=1, time=None, base_date_name='presale_end'))
assert rdw.datetime(event) == datetime(2017, 12, 26, 5, 0, 0, tzinfo=TOKYO)
assert rdw.to_string() == 'RELDATE/1/-/presale_end/'
# subevent base
se = event.subevents.create(name="SE1", date_from=datetime(2017, 11, 27, 5, 0, 0, tzinfo=TOKYO))
rdw = RelativeDateWrapper(RelativeDate(days_before=1, time=None, base_date_name='date_from'))
assert rdw.datetime(se) == datetime(2017, 11, 26, 5, 0, 0, tzinfo=TOKYO)
# presale_start is unset on subevent, default to event
rdw = RelativeDateWrapper(RelativeDate(days_before=1, time=None, base_date_name='presale_start'))
assert rdw.datetime(se) == datetime(2017, 11, 30, 5, 0, 0, tzinfo=TOKYO)
# presale_end is unset on all, default to date_from of subevent
rdw = RelativeDateWrapper(RelativeDate(days_before=1, time=None, base_date_name='presale_end'))
assert rdw.datetime(se) == datetime(2017, 11, 26, 5, 0, 0, tzinfo=TOKYO)
@pytest.mark.django_db
def test_relative_date_with_time(event):
rdw = RelativeDateWrapper(RelativeDate(days_before=1, time=time(8, 5, 13), base_date_name='date_from'))
assert rdw.to_string() == 'RELDATE/1/08:05:13/date_from/'
assert rdw.datetime(event) == datetime(2017, 12, 26, 8, 5, 13, tzinfo=TOKYO)
def test_unserialize():
d = datetime(2017, 12, 25, 10, 0, 0, tzinfo=TOKYO)
rdw = RelativeDateWrapper.from_string(d.isoformat())
assert rdw.data == d
rdw = RelativeDateWrapper.from_string('RELDATE/1/-/date_from/')
assert rdw.data == RelativeDate(days_before=1, time=None, base_date_name='date_from')
rdw = RelativeDateWrapper.from_string('RELDATE/1/18:05:13/date_from/')
assert rdw.data == RelativeDate(days_before=1, time=time(18, 5, 13), base_date_name='date_from')