forked from CGM_Public/pretix_original
Allow to explicitly disable products for certain subevents
This commit is contained in:
@@ -911,6 +911,19 @@ class OrderCreateSerializer(I18nAwareModelSerializer):
|
|||||||
if pos_data['voucher'].allow_ignore_quota or pos_data['voucher'].block_quota:
|
if pos_data['voucher'].allow_ignore_quota or pos_data['voucher'].block_quota:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
if pos_data.get('subevent'):
|
||||||
|
if pos_data.get('item').pk in pos_data['subevent'].item_overrides and pos_data['subevent'].item_overrides[pos_data['item'].pk].disabled:
|
||||||
|
errs[i]['item'] = [gettext_lazy('The product "{}" is not available on this date.').format(
|
||||||
|
str(pos_data.get('item'))
|
||||||
|
)]
|
||||||
|
if (
|
||||||
|
pos_data.get('variation') and pos_data['variation'].pk in pos_data['subevent'].var_overrides and
|
||||||
|
pos_data['subevent'].var_overrides[pos_data['variation'].pk].disabled
|
||||||
|
):
|
||||||
|
errs[i]['item'] = [gettext_lazy('The product "{}" is not available on this date.').format(
|
||||||
|
str(pos_data.get('item'))
|
||||||
|
)]
|
||||||
|
|
||||||
new_quotas = (pos_data.get('variation').quotas.filter(subevent=pos_data.get('subevent'))
|
new_quotas = (pos_data.get('variation').quotas.filter(subevent=pos_data.get('subevent'))
|
||||||
if pos_data.get('variation')
|
if pos_data.get('variation')
|
||||||
else pos_data.get('item').quotas.filter(subevent=pos_data.get('subevent')))
|
else pos_data.get('item').quotas.filter(subevent=pos_data.get('subevent')))
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
# Generated by Django 3.0.6 on 2020-06-20 16:33
|
||||||
|
|
||||||
|
import django_countries.fields
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
import pretix.helpers.countries
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('pretixbase', '0153_auto_20200528_1953'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='subeventitem',
|
||||||
|
name='disabled',
|
||||||
|
field=models.BooleanField(default=False),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='subeventitemvariation',
|
||||||
|
name='disabled',
|
||||||
|
field=models.BooleanField(default=False),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='invoiceaddress',
|
||||||
|
name='country',
|
||||||
|
field=django_countries.fields.CountryField(countries=pretix.helpers.countries.CachedCountries, max_length=2),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -1061,21 +1061,35 @@ class SubEvent(EventMixin, LoggedModel):
|
|||||||
return self.event.settings
|
return self.event.settings
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def item_price_overrides(self):
|
def item_overrides(self):
|
||||||
from .items import SubEventItem
|
from .items import SubEventItem
|
||||||
|
|
||||||
return {
|
return {
|
||||||
si.item_id: si.price
|
si.item_id: si
|
||||||
for si in SubEventItem.objects.filter(subevent=self, price__isnull=False)
|
for si in SubEventItem.objects.filter(subevent=self)
|
||||||
}
|
}
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def var_price_overrides(self):
|
def var_overrides(self):
|
||||||
from .items import SubEventItemVariation
|
from .items import SubEventItemVariation
|
||||||
|
|
||||||
|
return {
|
||||||
|
si.variation_id: si
|
||||||
|
for si in SubEventItemVariation.objects.filter(subevent=self)
|
||||||
|
}
|
||||||
|
|
||||||
|
@property
|
||||||
|
def item_price_overrides(self):
|
||||||
|
return {
|
||||||
|
si.item_id: si.price
|
||||||
|
for si in self.item_overrides.values() if si.price is not None
|
||||||
|
}
|
||||||
|
|
||||||
|
@property
|
||||||
|
def var_price_overrides(self):
|
||||||
return {
|
return {
|
||||||
si.variation_id: si.price
|
si.variation_id: si.price
|
||||||
for si in SubEventItemVariation.objects.filter(subevent=self, price__isnull=False)
|
for si in self.var_overrides.values() if si.price is not None
|
||||||
}
|
}
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|||||||
@@ -118,6 +118,7 @@ class SubEventItem(models.Model):
|
|||||||
subevent = models.ForeignKey('SubEvent', on_delete=models.CASCADE)
|
subevent = models.ForeignKey('SubEvent', on_delete=models.CASCADE)
|
||||||
item = models.ForeignKey('Item', on_delete=models.CASCADE)
|
item = models.ForeignKey('Item', on_delete=models.CASCADE)
|
||||||
price = models.DecimalField(max_digits=7, decimal_places=2, null=True, blank=True)
|
price = models.DecimalField(max_digits=7, decimal_places=2, null=True, blank=True)
|
||||||
|
disabled = models.BooleanField(default=False)
|
||||||
|
|
||||||
def delete(self, *args, **kwargs):
|
def delete(self, *args, **kwargs):
|
||||||
super().delete(*args, **kwargs)
|
super().delete(*args, **kwargs)
|
||||||
@@ -145,6 +146,7 @@ class SubEventItemVariation(models.Model):
|
|||||||
subevent = models.ForeignKey('SubEvent', on_delete=models.CASCADE)
|
subevent = models.ForeignKey('SubEvent', on_delete=models.CASCADE)
|
||||||
variation = models.ForeignKey('ItemVariation', on_delete=models.CASCADE)
|
variation = models.ForeignKey('ItemVariation', on_delete=models.CASCADE)
|
||||||
price = models.DecimalField(max_digits=7, decimal_places=2, null=True, blank=True)
|
price = models.DecimalField(max_digits=7, decimal_places=2, null=True, blank=True)
|
||||||
|
disabled = models.BooleanField(default=False)
|
||||||
|
|
||||||
def delete(self, *args, **kwargs):
|
def delete(self, *args, **kwargs):
|
||||||
super().delete(*args, **kwargs)
|
super().delete(*args, **kwargs)
|
||||||
|
|||||||
@@ -253,6 +253,12 @@ class CartManager:
|
|||||||
if self._sales_channel not in op.item.sales_channels:
|
if self._sales_channel not in op.item.sales_channels:
|
||||||
raise CartError(error_messages['unavailable'])
|
raise CartError(error_messages['unavailable'])
|
||||||
|
|
||||||
|
if op.subevent and op.item.pk in op.subevent.item_overrides and op.subevent.item_overrides[op.item.pk].disabled:
|
||||||
|
raise CartError(error_messages['not_for_sale'])
|
||||||
|
|
||||||
|
if op.subevent and op.variation and op.variation.pk in op.subevent.var_overrides and op.subevent.var_overrides[op.variation.pk].disabled:
|
||||||
|
raise CartError(error_messages['not_for_sale'])
|
||||||
|
|
||||||
if op.item.has_variations and not op.variation:
|
if op.item.has_variations and not op.variation:
|
||||||
raise CartError(error_messages['not_for_sale'])
|
raise CartError(error_messages['not_for_sale'])
|
||||||
|
|
||||||
|
|||||||
@@ -641,6 +641,16 @@ def _check_positions(event: Event, now_dt: datetime, positions: List[CartPositio
|
|||||||
delete(cp)
|
delete(cp)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
if cp.subevent and cp.item.pk in cp.subevent.item_overrides and cp.subevent.item_overrides[cp.item.pk].disabled:
|
||||||
|
err = err or error_messages['unavailable']
|
||||||
|
delete(cp)
|
||||||
|
continue
|
||||||
|
|
||||||
|
if cp.subevent and cp.variation and cp.variation.pk in cp.subevent.var_overrides and cp.subevent.var_overrides[cp.variation.pk].disabled:
|
||||||
|
err = err or error_messages['unavailable']
|
||||||
|
delete(cp)
|
||||||
|
continue
|
||||||
|
|
||||||
if cp.voucher:
|
if cp.voucher:
|
||||||
if cp.voucher.valid_until and cp.voucher.valid_until < now_dt:
|
if cp.voucher.valid_until and cp.voucher.valid_until < now_dt:
|
||||||
err = err or error_messages['voucher_expired']
|
err = err or error_messages['voucher_expired']
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ class SubEventItemForm(SubEventItemOrVariationFormMixin, forms.ModelForm):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = SubEventItem
|
model = SubEventItem
|
||||||
fields = ['price']
|
fields = ['price', 'disabled']
|
||||||
widgets = {
|
widgets = {
|
||||||
'price': forms.TextInput
|
'price': forms.TextInput
|
||||||
}
|
}
|
||||||
@@ -113,7 +113,7 @@ class SubEventItemVariationForm(SubEventItemOrVariationFormMixin, forms.ModelFor
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = SubEventItem
|
model = SubEventItem
|
||||||
fields = ['price']
|
fields = ['price', 'disabled']
|
||||||
widgets = {
|
widgets = {
|
||||||
'price': forms.TextInput
|
'price': forms.TextInput
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -461,7 +461,19 @@
|
|||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>{% trans "Item prices" %}</legend>
|
<legend>{% trans "Item prices" %}</legend>
|
||||||
{% for f in itemvar_forms %}
|
{% for f in itemvar_forms %}
|
||||||
{% bootstrap_field f.price addon_after=request.event.currency layout="control" %}
|
<div class="form-group">
|
||||||
|
<label class="col-md-3 control-label" for="id_{{ f.prefix }}-price">
|
||||||
|
{% if f.variation %}{{ f.item }} – {{ f.variation }}{% else %}{{ f.item }}{% endif %}
|
||||||
|
<br>
|
||||||
|
<span class="optional">{% trans "Optional" %}</span>
|
||||||
|
</label>
|
||||||
|
<div class="col-md-6">
|
||||||
|
{% bootstrap_field f.price addon_after=request.event.currency form_group_class="" layout="inline" %}
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3">
|
||||||
|
{% bootstrap_field f.disabled layout="inline" form_group_class="" %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<fieldset>
|
<fieldset>
|
||||||
|
|||||||
@@ -145,7 +145,19 @@
|
|||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>{% trans "Item prices" %}</legend>
|
<legend>{% trans "Item prices" %}</legend>
|
||||||
{% for f in itemvar_forms %}
|
{% for f in itemvar_forms %}
|
||||||
{% bootstrap_field f.price addon_after=request.event.currency layout="control" %}
|
<div class="form-group">
|
||||||
|
<label class="col-md-3 control-label" for="id_{{ f.prefix }}-price">
|
||||||
|
{% if f.variation %}{{ f.item }} – {{ f.variation }}{% else %}{{ f.item }}{% endif %}
|
||||||
|
<br>
|
||||||
|
<span class="optional">{% trans "Optional" %}</span>
|
||||||
|
</label>
|
||||||
|
<div class="col-md-6">
|
||||||
|
{% bootstrap_field f.price addon_after=request.event.currency form_group_class="" layout="inline" %}
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3">
|
||||||
|
{% bootstrap_field f.disabled layout="inline" form_group_class="" %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<fieldset>
|
<fieldset>
|
||||||
|
|||||||
@@ -338,11 +338,11 @@ class SubEventEditorMixin(MetaDataEditorMixin):
|
|||||||
|
|
||||||
if self.copy_from:
|
if self.copy_from:
|
||||||
se_item_instances = {
|
se_item_instances = {
|
||||||
sei.item_id: SubEventItem(item=sei.item, price=sei.price)
|
sei.item_id: SubEventItem(item=sei.item, price=sei.price, disabled=sei.disabled)
|
||||||
for sei in SubEventItem.objects.filter(subevent=self.copy_from).select_related('item')
|
for sei in SubEventItem.objects.filter(subevent=self.copy_from).select_related('item')
|
||||||
}
|
}
|
||||||
se_var_instances = {
|
se_var_instances = {
|
||||||
sei.variation_id: SubEventItemVariation(variation=sei.variation, price=sei.price)
|
sei.variation_id: SubEventItemVariation(variation=sei.variation, price=sei.price, disabled=sei.disabled)
|
||||||
for sei in SubEventItemVariation.objects.filter(subevent=self.copy_from).select_related('variation')
|
for sei in SubEventItemVariation.objects.filter(subevent=self.copy_from).select_related('variation')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,9 @@ from django.views.generic import TemplateView
|
|||||||
from pretix.base.channels import get_all_sales_channels
|
from pretix.base.channels import get_all_sales_channels
|
||||||
from pretix.base.models import ItemVariation, Quota, SeatCategoryMapping
|
from pretix.base.models import ItemVariation, Quota, SeatCategoryMapping
|
||||||
from pretix.base.models.event import SubEvent
|
from pretix.base.models.event import SubEvent
|
||||||
from pretix.base.models.items import ItemBundle
|
from pretix.base.models.items import (
|
||||||
|
ItemBundle, SubEventItem, SubEventItemVariation,
|
||||||
|
)
|
||||||
from pretix.base.services.quotas import QuotaAvailability
|
from pretix.base.services.quotas import QuotaAvailability
|
||||||
from pretix.multidomain.urlreverse import eventreverse
|
from pretix.multidomain.urlreverse import eventreverse
|
||||||
from pretix.presale.ical import get_ical
|
from pretix.presale.ical import get_ical
|
||||||
@@ -83,7 +85,17 @@ def get_grouped_items(event, subevent=None, voucher=None, channel='web', require
|
|||||||
)),
|
)),
|
||||||
)),
|
)),
|
||||||
Prefetch('variations', to_attr='available_variations',
|
Prefetch('variations', to_attr='available_variations',
|
||||||
queryset=ItemVariation.objects.using(settings.DATABASE_REPLICA).filter(active=True, quotas__isnull=False).prefetch_related(
|
queryset=ItemVariation.objects.using(settings.DATABASE_REPLICA).annotate(
|
||||||
|
subevent_disabled=Exists(
|
||||||
|
SubEventItemVariation.objects.filter(
|
||||||
|
variation_id=OuterRef('pk'),
|
||||||
|
subevent=subevent,
|
||||||
|
disabled=True,
|
||||||
|
)
|
||||||
|
),
|
||||||
|
).filter(
|
||||||
|
active=True, quotas__isnull=False, subevent_disabled=False
|
||||||
|
).prefetch_related(
|
||||||
Prefetch('quotas',
|
Prefetch('quotas',
|
||||||
to_attr='_subevent_quotas',
|
to_attr='_subevent_quotas',
|
||||||
queryset=event.quotas.using(settings.DATABASE_REPLICA).filter(subevent=subevent))
|
queryset=event.quotas.using(settings.DATABASE_REPLICA).filter(subevent=subevent))
|
||||||
@@ -91,14 +103,21 @@ def get_grouped_items(event, subevent=None, voucher=None, channel='web', require
|
|||||||
).annotate(
|
).annotate(
|
||||||
quotac=Count('quotas'),
|
quotac=Count('quotas'),
|
||||||
has_variations=Count('variations'),
|
has_variations=Count('variations'),
|
||||||
|
subevent_disabled=Exists(
|
||||||
|
SubEventItem.objects.filter(
|
||||||
|
item_id=OuterRef('pk'),
|
||||||
|
subevent=subevent,
|
||||||
|
disabled=True,
|
||||||
|
)
|
||||||
|
),
|
||||||
requires_seat=Exists(
|
requires_seat=Exists(
|
||||||
SeatCategoryMapping.objects.filter(
|
SeatCategoryMapping.objects.filter(
|
||||||
product_id=OuterRef('pk'),
|
product_id=OuterRef('pk'),
|
||||||
subevent=subevent
|
subevent=subevent
|
||||||
)
|
)
|
||||||
)
|
),
|
||||||
).filter(
|
).filter(
|
||||||
quotac__gt=0,
|
quotac__gt=0, subevent_disabled=False,
|
||||||
).order_by('category__position', 'category_id', 'position', 'name')
|
).order_by('category__position', 'category_id', 'position', 'name')
|
||||||
if require_seat:
|
if require_seat:
|
||||||
items = items.filter(requires_seat__gt=0)
|
items = items.filter(requires_seat__gt=0)
|
||||||
|
|||||||
@@ -2276,6 +2276,64 @@ def test_order_create_item_validation(token_client, organizer, event, item, item
|
|||||||
assert resp.data == {'positions': [{'variation': ['You should specify a variation for this item.']}]}
|
assert resp.data == {'positions': [{'variation': ['You should specify a variation for this item.']}]}
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_order_create_subevent_disabled(token_client, organizer, event, item, subevent, quota, question):
|
||||||
|
res = copy.deepcopy(ORDER_CREATE_PAYLOAD)
|
||||||
|
res['positions'][0]['item'] = item.pk
|
||||||
|
res['positions'][0]['answers'][0]['question'] = question.pk
|
||||||
|
res['positions'][0]['subevent'] = subevent.pk
|
||||||
|
s = item.subeventitem_set.create(subevent=subevent, disabled=True)
|
||||||
|
quota.subevent = subevent
|
||||||
|
quota.save()
|
||||||
|
resp = token_client.post(
|
||||||
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
||||||
|
organizer.slug, event.slug
|
||||||
|
), format='json', data=res
|
||||||
|
)
|
||||||
|
assert resp.status_code == 400
|
||||||
|
assert resp.data == {'positions': [{'item': ['The product "Budget Ticket" is not available on this date.']}]}
|
||||||
|
|
||||||
|
s.delete()
|
||||||
|
resp = token_client.post(
|
||||||
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
||||||
|
organizer.slug, event.slug
|
||||||
|
), format='json', data=res
|
||||||
|
)
|
||||||
|
assert resp.status_code == 201
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_order_create_subevent_variation_disabled(token_client, organizer, event, item, subevent, quota, question):
|
||||||
|
with scopes_disabled():
|
||||||
|
item2 = event.items.create(name="Budget Ticket", default_price=23)
|
||||||
|
var = item2.variations.create(default_price=12, value="XS")
|
||||||
|
res = copy.deepcopy(ORDER_CREATE_PAYLOAD)
|
||||||
|
res['positions'][0]['item'] = item2.pk
|
||||||
|
res['positions'][0]['variation'] = var.pk
|
||||||
|
res['positions'][0]['answers'][0]['question'] = question.pk
|
||||||
|
res['positions'][0]['subevent'] = subevent.pk
|
||||||
|
s = var.subeventitemvariation_set.create(subevent=subevent, disabled=True)
|
||||||
|
quota.subevent = subevent
|
||||||
|
quota.items.add(item2)
|
||||||
|
quota.variations.add(var)
|
||||||
|
quota.save()
|
||||||
|
resp = token_client.post(
|
||||||
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
||||||
|
organizer.slug, event.slug
|
||||||
|
), format='json', data=res
|
||||||
|
)
|
||||||
|
assert resp.status_code == 400
|
||||||
|
assert resp.data == {'positions': [{'item': ['The product "Budget Ticket" is not available on this date.']}]}
|
||||||
|
|
||||||
|
s.delete()
|
||||||
|
resp = token_client.post(
|
||||||
|
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
||||||
|
organizer.slug, event.slug
|
||||||
|
), format='json', data=res
|
||||||
|
)
|
||||||
|
assert resp.status_code == 201
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.django_db
|
@pytest.mark.django_db
|
||||||
def test_order_create_positionids_addons(token_client, organizer, event, item, quota):
|
def test_order_create_positionids_addons(token_client, organizer, event, item, quota):
|
||||||
res = copy.deepcopy(ORDER_CREATE_PAYLOAD)
|
res = copy.deepcopy(ORDER_CREATE_PAYLOAD)
|
||||||
|
|||||||
@@ -477,6 +477,22 @@ class CartTest(CartTestMixin, TestCase):
|
|||||||
objs = list(CartPosition.objects.filter(cart_id=self.session_key, event=self.event))
|
objs = list(CartPosition.objects.filter(cart_id=self.session_key, event=self.event))
|
||||||
self.assertEqual(len(objs), 0)
|
self.assertEqual(len(objs), 0)
|
||||||
|
|
||||||
|
def test_subevent_disabled(self):
|
||||||
|
self.event.has_subevents = True
|
||||||
|
self.event.save()
|
||||||
|
with scopes_disabled():
|
||||||
|
se = self.event.subevents.create(name='Foo', date_from=now(), active=True)
|
||||||
|
q = se.quotas.create(name="foo", size=None, event=self.event)
|
||||||
|
q.items.add(self.ticket)
|
||||||
|
SubEventItem.objects.create(subevent=se, item=self.ticket, price=42, disabled=True)
|
||||||
|
self.client.post('/%s/%s/cart/add' % (self.orga.slug, self.event.slug), {
|
||||||
|
'item_%d' % self.ticket.id: '1',
|
||||||
|
'subevent': se.pk
|
||||||
|
}, follow=False)
|
||||||
|
with scopes_disabled():
|
||||||
|
objs = list(CartPosition.objects.filter(cart_id=self.session_key, event=self.event))
|
||||||
|
self.assertEqual(len(objs), 0)
|
||||||
|
|
||||||
def test_subevent_price(self):
|
def test_subevent_price(self):
|
||||||
self.event.has_subevents = True
|
self.event.has_subevents = True
|
||||||
self.event.save()
|
self.event.save()
|
||||||
@@ -613,6 +629,22 @@ class CartTest(CartTestMixin, TestCase):
|
|||||||
self.assertEqual(objs[0].variation, self.shirt_red)
|
self.assertEqual(objs[0].variation, self.shirt_red)
|
||||||
self.assertEqual(objs[0].price, 16)
|
self.assertEqual(objs[0].price, 16)
|
||||||
|
|
||||||
|
def test_subevent_variation_disabled(self):
|
||||||
|
self.event.has_subevents = True
|
||||||
|
self.event.save()
|
||||||
|
with scopes_disabled():
|
||||||
|
se = self.event.subevents.create(name='Foo', date_from=now(), active=True)
|
||||||
|
q = se.quotas.create(name="foo", size=None, event=self.event)
|
||||||
|
q.variations.add(self.shirt_red)
|
||||||
|
SubEventItemVariation.objects.create(subevent=se, variation=self.shirt_red, price=42, disabled=True)
|
||||||
|
self.client.post('/%s/%s/cart/add' % (self.orga.slug, self.event.slug), {
|
||||||
|
'variation_%d_%d' % (self.shirt.id, self.shirt_red.id): '1',
|
||||||
|
'subevent': se.pk
|
||||||
|
}, follow=False)
|
||||||
|
with scopes_disabled():
|
||||||
|
objs = list(CartPosition.objects.filter(cart_id=self.session_key, event=self.event))
|
||||||
|
self.assertEqual(len(objs), 0)
|
||||||
|
|
||||||
def test_subevent_variation_price(self):
|
def test_subevent_variation_price(self):
|
||||||
self.event.has_subevents = True
|
self.event.has_subevents = True
|
||||||
self.event.save()
|
self.event.save()
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ from pretix.base.models import (
|
|||||||
SeatingPlan, Voucher,
|
SeatingPlan, Voucher,
|
||||||
)
|
)
|
||||||
from pretix.base.models.items import (
|
from pretix.base.models.items import (
|
||||||
ItemAddOn, ItemBundle, ItemVariation, SubEventItem,
|
ItemAddOn, ItemBundle, ItemVariation, SubEventItem, SubEventItemVariation,
|
||||||
)
|
)
|
||||||
from pretix.base.services.orders import OrderError, _perform_order
|
from pretix.base.services.orders import OrderError, _perform_order
|
||||||
from pretix.testutils.scope import classscope
|
from pretix.testutils.scope import classscope
|
||||||
@@ -1465,6 +1465,43 @@ class CheckoutTestCase(BaseCheckoutTestCase, TestCase):
|
|||||||
cr1 = CartPosition.objects.get(id=cr1.id)
|
cr1 = CartPosition.objects.get(id=cr1.id)
|
||||||
self.assertEqual(cr1.price, 24)
|
self.assertEqual(cr1.price, 24)
|
||||||
|
|
||||||
|
def test_subevent_disabled(self):
|
||||||
|
self.event.has_subevents = True
|
||||||
|
self.event.save()
|
||||||
|
with scopes_disabled():
|
||||||
|
se = self.event.subevents.create(name='Foo', date_from=now())
|
||||||
|
q = se.quotas.create(name="foo", size=None, event=self.event)
|
||||||
|
q.items.add(self.ticket)
|
||||||
|
SubEventItem.objects.create(subevent=se, item=self.ticket, price=24, disabled=True)
|
||||||
|
cr1 = CartPosition.objects.create(
|
||||||
|
event=self.event, cart_id=self.session_key, item=self.ticket,
|
||||||
|
price=23, expires=now() - timedelta(minutes=10), subevent=se
|
||||||
|
)
|
||||||
|
self._set_session('payment', 'banktransfer')
|
||||||
|
|
||||||
|
self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True)
|
||||||
|
with scopes_disabled():
|
||||||
|
assert not CartPosition.objects.filter(id=cr1.id).exists()
|
||||||
|
|
||||||
|
def test_subevent_variation_disabled(self):
|
||||||
|
self.event.has_subevents = True
|
||||||
|
self.event.save()
|
||||||
|
with scopes_disabled():
|
||||||
|
se = self.event.subevents.create(name='Foo', date_from=now())
|
||||||
|
q = se.quotas.create(name="foo", size=None, event=self.event)
|
||||||
|
q.items.add(self.workshop2)
|
||||||
|
q.variations.add(self.workshop2b)
|
||||||
|
SubEventItemVariation.objects.create(subevent=se, variation=self.workshop2b, price=24, disabled=True)
|
||||||
|
cr1 = CartPosition.objects.create(
|
||||||
|
event=self.event, cart_id=self.session_key, item=self.workshop2, variation=self.workshop2b,
|
||||||
|
price=23, expires=now() - timedelta(minutes=10), subevent=se
|
||||||
|
)
|
||||||
|
self._set_session('payment', 'banktransfer')
|
||||||
|
|
||||||
|
self.client.post('/%s/%s/checkout/confirm/' % (self.orga.slug, self.event.slug), follow=True)
|
||||||
|
with scopes_disabled():
|
||||||
|
assert not CartPosition.objects.filter(id=cr1.id).exists()
|
||||||
|
|
||||||
def test_addon_price_included(self):
|
def test_addon_price_included(self):
|
||||||
with scopes_disabled():
|
with scopes_disabled():
|
||||||
ItemAddOn.objects.create(base_item=self.ticket, addon_category=self.workshopcat, min_count=1,
|
ItemAddOn.objects.create(base_item=self.ticket, addon_category=self.workshopcat, min_count=1,
|
||||||
|
|||||||
@@ -255,6 +255,24 @@ class ItemDisplayTest(EventTestMixin, SoupTest):
|
|||||||
resp = self.client.get('/%s/%s/%d/' % (self.orga.slug, self.event.slug, se2.pk))
|
resp = self.client.get('/%s/%s/%d/' % (self.orga.slug, self.event.slug, se2.pk))
|
||||||
self.assertNotIn("Early-bird", resp.rendered_content)
|
self.assertNotIn("Early-bird", resp.rendered_content)
|
||||||
|
|
||||||
|
def test_subevent_disabled(self):
|
||||||
|
self.event.has_subevents = True
|
||||||
|
self.event.save()
|
||||||
|
with scopes_disabled():
|
||||||
|
se1 = self.event.subevents.create(name='Foo', date_from=now(), active=True)
|
||||||
|
se2 = self.event.subevents.create(name='Foo', date_from=now(), active=True)
|
||||||
|
item = Item.objects.create(event=self.event, name='Early-bird ticket', default_price=15)
|
||||||
|
q = Quota.objects.create(event=self.event, name='Quota', size=2, subevent=se1)
|
||||||
|
q.items.add(item)
|
||||||
|
q = Quota.objects.create(event=self.event, name='Quota', size=2, subevent=se2)
|
||||||
|
q.items.add(item)
|
||||||
|
SubEventItem.objects.create(subevent=se1, item=item, price=12, disabled=True)
|
||||||
|
|
||||||
|
resp = self.client.get('/%s/%s/%d/' % (self.orga.slug, self.event.slug, se1.pk))
|
||||||
|
self.assertNotIn("Early-bird", resp.rendered_content)
|
||||||
|
resp = self.client.get('/%s/%s/%d/' % (self.orga.slug, self.event.slug, se2.pk))
|
||||||
|
self.assertIn("Early-bird", resp.rendered_content)
|
||||||
|
|
||||||
def test_subevent_prices(self):
|
def test_subevent_prices(self):
|
||||||
self.event.has_subevents = True
|
self.event.has_subevents = True
|
||||||
self.event.save()
|
self.event.save()
|
||||||
@@ -300,6 +318,27 @@ class ItemDisplayTest(EventTestMixin, SoupTest):
|
|||||||
self.assertNotIn("12.00", resp.rendered_content)
|
self.assertNotIn("12.00", resp.rendered_content)
|
||||||
self.assertNotIn("15.00", resp.rendered_content)
|
self.assertNotIn("15.00", resp.rendered_content)
|
||||||
|
|
||||||
|
def test_variations_subevent_disabled(self):
|
||||||
|
self.event.has_subevents = True
|
||||||
|
self.event.save()
|
||||||
|
with scopes_disabled():
|
||||||
|
se1 = self.event.subevents.create(name='Foo', date_from=now(), active=True)
|
||||||
|
se2 = self.event.subevents.create(name='Foo', date_from=now(), active=True)
|
||||||
|
item = Item.objects.create(event=self.event, name='Early-bird ticket', default_price=15)
|
||||||
|
v = ItemVariation.objects.create(item=item, value='Blue')
|
||||||
|
q = Quota.objects.create(event=self.event, name='Quota', size=2, subevent=se1)
|
||||||
|
q.items.add(item)
|
||||||
|
q.variations.add(v)
|
||||||
|
q = Quota.objects.create(event=self.event, name='Quota', size=2, subevent=se2)
|
||||||
|
q.items.add(item)
|
||||||
|
q.variations.add(v)
|
||||||
|
SubEventItemVariation.objects.create(subevent=se1, variation=v, disabled=True)
|
||||||
|
|
||||||
|
resp = self.client.get('/%s/%s/%d/' % (self.orga.slug, self.event.slug, se1.pk))
|
||||||
|
self.assertNotIn("Early-bird", resp.rendered_content)
|
||||||
|
resp = self.client.get('/%s/%s/%d/' % (self.orga.slug, self.event.slug, se2.pk))
|
||||||
|
self.assertIn("Early-bird", resp.rendered_content)
|
||||||
|
|
||||||
def test_no_variations_in_quota(self):
|
def test_no_variations_in_quota(self):
|
||||||
with scopes_disabled():
|
with scopes_disabled():
|
||||||
c = ItemCategory.objects.create(event=self.event, name="Entry tickets", position=0)
|
c = ItemCategory.objects.create(event=self.event, name="Entry tickets", position=0)
|
||||||
|
|||||||
Reference in New Issue
Block a user