mirror of
https://github.com/pretix/pretix.git
synced 2026-08-10 10:47:00 +00:00
Replace Item.hidden_if_available with relationship to other Item (#3686)
* draft * Implementation that is closer to old one * Fix tests * Add tests * Update src/pretix/control/forms/item.py Co-authored-by: Richard Schreiber <schreiber@rami.io> * Review notes --------- Co-authored-by: Richard Schreiber <schreiber@rami.io>
This commit is contained in:
co-authored by
Richard Schreiber
parent
3d68c83907
commit
3af2342d7b
@@ -52,9 +52,12 @@ available_from datetime The first dat
|
||||
(or ``null``).
|
||||
available_until datetime The last date time at which this item can be bought
|
||||
(or ``null``).
|
||||
hidden_if_available integer The internal ID of a quota object, or ``null``. If
|
||||
hidden_if_available integer **DEPRECATED** The internal ID of a quota object, or ``null``. If
|
||||
set, this item won't be shown publicly as long as this
|
||||
quota is available.
|
||||
hidden_if_item_available integer The internal ID of a different item, or ``null``. If
|
||||
set, this item won't be shown publicly as long as this
|
||||
other item is available.
|
||||
require_voucher boolean If ``true``, this item can only be bought using a
|
||||
voucher that is specifically assigned to this item.
|
||||
hide_without_voucher boolean If ``true``, this item is only shown during the voucher
|
||||
@@ -204,6 +207,11 @@ meta_data object Values set fo
|
||||
|
||||
The ``free_price_suggestion`` and ``variations[x].free_price_suggestion`` attributes have been added.
|
||||
|
||||
.. versionchanged:: 2023.10
|
||||
|
||||
The ``hidden_if_item_available`` attributes has been added, the ``hidden_if_available`` attribute has been
|
||||
deprecated.
|
||||
|
||||
Notes
|
||||
-----
|
||||
|
||||
@@ -268,6 +276,7 @@ Endpoints
|
||||
"available_from": null,
|
||||
"available_until": null,
|
||||
"hidden_if_available": null,
|
||||
"hidden_if_item_available": null,
|
||||
"require_voucher": false,
|
||||
"hide_without_voucher": false,
|
||||
"allow_cancel": true,
|
||||
@@ -402,6 +411,7 @@ Endpoints
|
||||
"available_from": null,
|
||||
"available_until": null,
|
||||
"hidden_if_available": null,
|
||||
"hidden_if_item_available": null,
|
||||
"require_voucher": false,
|
||||
"hide_without_voucher": false,
|
||||
"allow_cancel": true,
|
||||
@@ -517,6 +527,7 @@ Endpoints
|
||||
"available_from": null,
|
||||
"available_until": null,
|
||||
"hidden_if_available": null,
|
||||
"hidden_if_item_available": null,
|
||||
"require_voucher": false,
|
||||
"hide_without_voucher": false,
|
||||
"allow_cancel": true,
|
||||
@@ -619,6 +630,7 @@ Endpoints
|
||||
"available_from": null,
|
||||
"available_until": null,
|
||||
"hidden_if_available": null,
|
||||
"hidden_if_item_available": null,
|
||||
"require_voucher": false,
|
||||
"hide_without_voucher": false,
|
||||
"allow_cancel": true,
|
||||
@@ -753,6 +765,7 @@ Endpoints
|
||||
"available_from": null,
|
||||
"available_until": null,
|
||||
"hidden_if_available": null,
|
||||
"hidden_if_item_available": null,
|
||||
"require_voucher": false,
|
||||
"hide_without_voucher": false,
|
||||
"generate_tickets": null,
|
||||
|
||||
@@ -239,7 +239,8 @@ class ItemSerializer(I18nAwareModelSerializer):
|
||||
'require_voucher', 'hide_without_voucher', 'allow_cancel', 'require_bundling',
|
||||
'min_per_order', 'max_per_order', 'checkin_attention', 'has_variations', 'variations',
|
||||
'addons', 'bundles', 'original_price', 'require_approval', 'generate_tickets',
|
||||
'show_quota_left', 'hidden_if_available', 'allow_waitinglist', 'issue_giftcard', 'meta_data',
|
||||
'show_quota_left', 'hidden_if_available', 'hidden_if_item_available', 'allow_waitinglist',
|
||||
'issue_giftcard', 'meta_data',
|
||||
'require_membership', 'require_membership_types', 'require_membership_hidden', 'grant_membership_type',
|
||||
'grant_membership_duration_like_event', 'grant_membership_duration_days',
|
||||
'grant_membership_duration_months', 'validity_mode', 'validity_fixed_from', 'validity_fixed_until',
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
# Generated by Django 4.2.4 on 2023-10-30 11:50
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("pretixbase", "0248_item_free_price_suggestion"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="item",
|
||||
name="hidden_if_item_available",
|
||||
field=models.ForeignKey(
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_NULL,
|
||||
to="pretixbase.item",
|
||||
),
|
||||
),
|
||||
]
|
||||
@@ -857,6 +857,10 @@ class Event(EventMixin, LoggedModel):
|
||||
v.item = i
|
||||
v.save(force_insert=True)
|
||||
|
||||
for i in self.items.filter(hidden_if_item_available__isnull=False):
|
||||
i.hidden_if_item_available = item_map[i.hidden_if_item_available_id]
|
||||
i.save()
|
||||
|
||||
for imv in ItemMetaValue.objects.filter(item__event=other):
|
||||
imv.pk = None
|
||||
imv.property = item_meta_properties_map[imv.property_id]
|
||||
|
||||
@@ -494,13 +494,24 @@ class Item(LoggedModel):
|
||||
'Quota',
|
||||
null=True, blank=True,
|
||||
on_delete=models.SET_NULL,
|
||||
verbose_name=_("Only show after sellout of"),
|
||||
verbose_name=pgettext_lazy("hidden_if_available_legacy", "Only show after sellout of"),
|
||||
help_text=_("If you select a quota here, this product will only be shown when that quota is "
|
||||
"unavailable. If combined with the option to hide sold-out products, this allows you to "
|
||||
"swap out products for more expensive ones once they are sold out. There might be a short period "
|
||||
"in which both products are visible while all tickets in the referenced quota are reserved, "
|
||||
"but not yet sold.")
|
||||
)
|
||||
hidden_if_item_available = models.ForeignKey(
|
||||
'Item',
|
||||
null=True, blank=True,
|
||||
on_delete=models.SET_NULL,
|
||||
verbose_name=_("Only show after sellout of"),
|
||||
help_text=_("If you select a product here, this product will only be shown when that product is "
|
||||
"sold out. If combined with the option to hide sold-out products, this allows you to "
|
||||
"swap out products for more expensive ones once the cheaper option is sold out. There might "
|
||||
"be a short period in which both products are visible while all tickets of the referenced "
|
||||
"product are reserved, but not yet sold.")
|
||||
)
|
||||
require_voucher = models.BooleanField(
|
||||
verbose_name=_('This product can only be bought using a voucher.'),
|
||||
default=False,
|
||||
|
||||
@@ -45,7 +45,7 @@ from django.db.models import Max
|
||||
from django.forms.formsets import DELETION_FIELD_NAME
|
||||
from django.urls import reverse
|
||||
from django.utils.functional import cached_property
|
||||
from django.utils.html import escape
|
||||
from django.utils.html import escape, format_html
|
||||
from django.utils.safestring import mark_safe
|
||||
from django.utils.translation import (
|
||||
gettext as __, gettext_lazy as _, pgettext_lazy,
|
||||
@@ -387,6 +387,7 @@ class ItemCreateForm(I18nModelForm):
|
||||
'allow_waitinglist',
|
||||
'show_quota_left',
|
||||
'hidden_if_available',
|
||||
'hidden_if_item_available',
|
||||
'require_bundling',
|
||||
'require_membership',
|
||||
'grant_membership_type',
|
||||
@@ -550,19 +551,43 @@ class ItemUpdateForm(I18nModelForm):
|
||||
widget=forms.CheckboxSelectMultiple
|
||||
)
|
||||
change_decimal_field(self.fields['default_price'], self.event.currency)
|
||||
self.fields['hidden_if_available'].queryset = self.event.quotas.all()
|
||||
self.fields['hidden_if_available'].widget = Select2(
|
||||
|
||||
if self.instance.hidden_if_available_id:
|
||||
self.fields['hidden_if_available'].queryset = self.event.quotas.all()
|
||||
self.fields['hidden_if_available'].help_text = format_html(
|
||||
"<strong>{}</strong> {}",
|
||||
_("This option is deprecated. For new products, use the newer option below that refers to another "
|
||||
"product instead of a quota."),
|
||||
self.fields['hidden_if_available'].help_text
|
||||
)
|
||||
self.fields['hidden_if_available'].widget = Select2(
|
||||
attrs={
|
||||
'data-model-select2': 'generic',
|
||||
'data-select2-url': reverse('control:event.items.quotas.select2', kwargs={
|
||||
'event': self.event.slug,
|
||||
'organizer': self.event.organizer.slug,
|
||||
}),
|
||||
'data-placeholder': _('Shown independently of other products')
|
||||
}
|
||||
)
|
||||
self.fields['hidden_if_available'].widget.choices = self.fields['hidden_if_available'].choices
|
||||
self.fields['hidden_if_available'].required = False
|
||||
else:
|
||||
del self.fields['hidden_if_available']
|
||||
|
||||
self.fields['hidden_if_item_available'].queryset = self.event.items.exclude(id=self.instance.id)
|
||||
self.fields['hidden_if_item_available'].widget = Select2(
|
||||
attrs={
|
||||
'data-model-select2': 'generic',
|
||||
'data-select2-url': reverse('control:event.items.quotas.select2', kwargs={
|
||||
'data-select2-url': reverse('control:event.items.select2', kwargs={
|
||||
'event': self.event.slug,
|
||||
'organizer': self.event.organizer.slug,
|
||||
}),
|
||||
'data-placeholder': _('Shown independently of other products')
|
||||
}
|
||||
)
|
||||
self.fields['hidden_if_available'].widget.choices = self.fields['hidden_if_available'].choices
|
||||
self.fields['hidden_if_available'].required = False
|
||||
self.fields['hidden_if_item_available'].widget.choices = self.fields['hidden_if_item_available'].choices
|
||||
self.fields['hidden_if_item_available'].required = False
|
||||
|
||||
self.fields['category'].queryset = self.instance.event.categories.all()
|
||||
self.fields['category'].widget = Select2(
|
||||
@@ -683,6 +708,7 @@ class ItemUpdateForm(I18nModelForm):
|
||||
'require_bundling',
|
||||
'show_quota_left',
|
||||
'hidden_if_available',
|
||||
'hidden_if_item_available',
|
||||
'issue_giftcard',
|
||||
'require_membership',
|
||||
'require_membership_types',
|
||||
@@ -709,6 +735,7 @@ class ItemUpdateForm(I18nModelForm):
|
||||
'validity_fixed_from': SplitDateTimeField,
|
||||
'validity_fixed_until': SplitDateTimeField,
|
||||
'hidden_if_available': SafeModelChoiceField,
|
||||
'hidden_if_item_available': SafeModelChoiceField,
|
||||
'grant_membership_type': SafeModelChoiceField,
|
||||
'require_membership_types': SafeModelMultipleChoiceField,
|
||||
}
|
||||
|
||||
@@ -32,6 +32,14 @@
|
||||
{% endblocktrans %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if not request.event.has_subevents and object.hidden_if_item_available and object.hidden_if_item_available.check_quotas.0 == 100 %}
|
||||
<div class="alert alert-warning">
|
||||
{% blocktrans trimmed %}
|
||||
This product is currently not being shown since you configured below that it should only be visible
|
||||
if a certain other product is already sold out.
|
||||
{% endblocktrans %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% block inside %}
|
||||
{% endblock %}
|
||||
|
||||
@@ -169,7 +169,10 @@
|
||||
{% endif %}
|
||||
{% bootstrap_field form.allow_cancel layout="control" %}
|
||||
{% bootstrap_field form.allow_waitinglist layout="control" %}
|
||||
{% bootstrap_field form.hidden_if_available layout="control" %}
|
||||
{% if form.hidden_if_available %}
|
||||
{% bootstrap_field form.hidden_if_available layout="control" %}
|
||||
{% endif %}
|
||||
{% bootstrap_field form.hidden_if_item_available layout="control" %}
|
||||
</fieldset>
|
||||
{% for v in formsets.values %}
|
||||
<fieldset>
|
||||
|
||||
@@ -4255,7 +4255,7 @@ msgstr "Produktbild"
|
||||
|
||||
#: pretix/base/models/items.py:491
|
||||
msgid "Only show after sellout of"
|
||||
msgstr "Nicht anzeigen, wenn anderes Kontingent verfügbar"
|
||||
msgstr "Nicht anzeigen, wenn anderes Produkt verfügbar"
|
||||
|
||||
#: pretix/base/models/items.py:492
|
||||
msgid ""
|
||||
|
||||
@@ -4252,7 +4252,7 @@ msgstr "Produktbild"
|
||||
|
||||
#: pretix/base/models/items.py:491
|
||||
msgid "Only show after sellout of"
|
||||
msgstr "Nicht anzeigen, wenn anderes Kontingent verfügbar"
|
||||
msgstr "Nicht anzeigen, wenn anderes Produkt verfügbar"
|
||||
|
||||
#: pretix/base/models/items.py:492
|
||||
msgid ""
|
||||
|
||||
@@ -127,52 +127,77 @@ def get_grouped_items(event, subevent=None, voucher=None, channel='web', require
|
||||
else:
|
||||
prefetch_membership_types = []
|
||||
|
||||
prefetch_var = Prefetch(
|
||||
'variations',
|
||||
to_attr='available_variations',
|
||||
queryset=ItemVariation.objects.using(settings.DATABASE_REPLICA).annotate(
|
||||
subevent_disabled=Exists(
|
||||
SubEventItemVariation.objects.filter(
|
||||
Q(disabled=True) | Q(available_from__gt=now()) | Q(available_until__lt=now()),
|
||||
variation_id=OuterRef('pk'),
|
||||
subevent=subevent,
|
||||
)
|
||||
),
|
||||
).filter(
|
||||
variation_q,
|
||||
active=True,
|
||||
sales_channels__contains=channel,
|
||||
quotas__isnull=False,
|
||||
subevent_disabled=False
|
||||
).prefetch_related(
|
||||
*prefetch_membership_types,
|
||||
Prefetch('quotas',
|
||||
to_attr='_subevent_quotas',
|
||||
queryset=event.quotas.using(settings.DATABASE_REPLICA).filter(
|
||||
subevent=subevent))
|
||||
).distinct()
|
||||
)
|
||||
prefetch_quotas = Prefetch(
|
||||
'quotas',
|
||||
to_attr='_subevent_quotas',
|
||||
queryset=event.quotas.using(settings.DATABASE_REPLICA).filter(subevent=subevent)
|
||||
)
|
||||
prefetch_bundles = Prefetch(
|
||||
'bundles',
|
||||
queryset=ItemBundle.objects.using(settings.DATABASE_REPLICA).prefetch_related(
|
||||
Prefetch('bundled_item',
|
||||
queryset=event.items.using(settings.DATABASE_REPLICA).select_related(
|
||||
'tax_rule').prefetch_related(
|
||||
Prefetch('quotas',
|
||||
to_attr='_subevent_quotas',
|
||||
queryset=event.quotas.using(settings.DATABASE_REPLICA).filter(
|
||||
subevent=subevent)),
|
||||
)),
|
||||
Prefetch('bundled_variation',
|
||||
queryset=ItemVariation.objects.using(
|
||||
settings.DATABASE_REPLICA
|
||||
).select_related('item', 'item__tax_rule').filter(item__event=event).prefetch_related(
|
||||
Prefetch('quotas',
|
||||
to_attr='_subevent_quotas',
|
||||
queryset=event.quotas.using(settings.DATABASE_REPLICA).filter(
|
||||
subevent=subevent)),
|
||||
)),
|
||||
)
|
||||
)
|
||||
|
||||
items = base_qs.using(settings.DATABASE_REPLICA).filter_available(channel=channel, voucher=voucher, allow_addons=allow_addons).select_related(
|
||||
'category', 'tax_rule', # for re-grouping
|
||||
'hidden_if_available',
|
||||
).prefetch_related(
|
||||
*prefetch_membership_types,
|
||||
Prefetch('quotas',
|
||||
to_attr='_subevent_quotas',
|
||||
queryset=event.quotas.using(settings.DATABASE_REPLICA).filter(subevent=subevent)),
|
||||
Prefetch('bundles',
|
||||
queryset=ItemBundle.objects.using(settings.DATABASE_REPLICA).prefetch_related(
|
||||
Prefetch('bundled_item',
|
||||
queryset=event.items.using(settings.DATABASE_REPLICA).select_related('tax_rule').prefetch_related(
|
||||
Prefetch('quotas',
|
||||
to_attr='_subevent_quotas',
|
||||
queryset=event.quotas.using(settings.DATABASE_REPLICA).filter(subevent=subevent)),
|
||||
)),
|
||||
Prefetch('bundled_variation',
|
||||
queryset=ItemVariation.objects.using(
|
||||
settings.DATABASE_REPLICA
|
||||
).select_related('item', 'item__tax_rule').filter(item__event=event).prefetch_related(
|
||||
Prefetch('quotas',
|
||||
to_attr='_subevent_quotas',
|
||||
queryset=event.quotas.using(settings.DATABASE_REPLICA).filter(subevent=subevent)),
|
||||
)),
|
||||
)),
|
||||
Prefetch('variations', to_attr='available_variations',
|
||||
queryset=ItemVariation.objects.using(settings.DATABASE_REPLICA).annotate(
|
||||
subevent_disabled=Exists(
|
||||
SubEventItemVariation.objects.filter(
|
||||
Q(disabled=True) | Q(available_from__gt=now()) | Q(available_until__lt=now()),
|
||||
variation_id=OuterRef('pk'),
|
||||
subevent=subevent,
|
||||
)
|
||||
),
|
||||
).filter(
|
||||
variation_q,
|
||||
active=True,
|
||||
sales_channels__contains=channel,
|
||||
quotas__isnull=False,
|
||||
subevent_disabled=False
|
||||
).prefetch_related(
|
||||
*prefetch_membership_types,
|
||||
Prefetch('quotas',
|
||||
to_attr='_subevent_quotas',
|
||||
queryset=event.quotas.using(settings.DATABASE_REPLICA).filter(subevent=subevent))
|
||||
).distinct()),
|
||||
Prefetch(
|
||||
'hidden_if_item_available',
|
||||
queryset=event.items.annotate(
|
||||
has_variations=Count('variations'),
|
||||
).prefetch_related(
|
||||
prefetch_var,
|
||||
prefetch_quotas,
|
||||
prefetch_bundles,
|
||||
)
|
||||
),
|
||||
prefetch_quotas,
|
||||
prefetch_var,
|
||||
prefetch_bundles,
|
||||
).annotate(
|
||||
quotac=Count('quotas'),
|
||||
has_variations=Count('variations'),
|
||||
@@ -256,6 +281,19 @@ def get_grouped_items(event, subevent=None, voucher=None, channel='web', require
|
||||
item._remove = True
|
||||
continue
|
||||
|
||||
if item.hidden_if_item_available:
|
||||
if item.hidden_if_item_available.has_variations:
|
||||
dependency_available = any(
|
||||
var.check_quotas(subevent=subevent, _cache=quota_cache, include_bundled=True)[0] == Quota.AVAILABILITY_OK
|
||||
for var in item.hidden_if_item_available.available_variations
|
||||
)
|
||||
else:
|
||||
q = item.hidden_if_item_available.check_quotas(subevent=subevent, _cache=quota_cache, include_bundled=True)
|
||||
dependency_available = q[0] == Quota.AVAILABILITY_OK
|
||||
if dependency_available:
|
||||
item._remove = True
|
||||
continue
|
||||
|
||||
if item.require_membership and item.require_membership_hidden:
|
||||
if not memberships or not any([m.membership_type in item.require_membership_types.all() for m in memberships]):
|
||||
item._remove = True
|
||||
|
||||
@@ -277,6 +277,7 @@ TEST_ITEM_RES = {
|
||||
"min_per_order": None,
|
||||
"max_per_order": None,
|
||||
"hidden_if_available": None,
|
||||
"hidden_if_item_available": None,
|
||||
"checkin_attention": False,
|
||||
"has_variations": False,
|
||||
"require_approval": False,
|
||||
|
||||
@@ -71,7 +71,8 @@ def test_full_clone_same_organizer():
|
||||
# todo: test that item pictures are copied, not linked
|
||||
ItemMetaValue.objects.create(item=item1, property=item_meta, value="Foo")
|
||||
assert item1.meta_data
|
||||
item2 = event.items.create(category=category, tax_rule=tax_rule, name="T-shirt", default_price=15)
|
||||
item2 = event.items.create(category=category, tax_rule=tax_rule, name="T-shirt", default_price=15,
|
||||
hidden_if_item_available=item1)
|
||||
item2v = item2.variations.create(value="red", default_price=15)
|
||||
item2v.meta_values.create(property=item_meta, value="Bar")
|
||||
item2.require_membership_types.add(membership_type)
|
||||
@@ -156,6 +157,7 @@ def test_full_clone_same_organizer():
|
||||
assert copied_item1.addons.get().addon_category == copied_event.categories.get()
|
||||
assert copied_item1.bundles.get().bundled_item == copied_item2
|
||||
assert copied_item1.bundles.get().bundled_variation == copied_item2.variations.get()
|
||||
assert copied_item2.hidden_if_item_available == copied_item1
|
||||
assert copied_q1.items.get() == copied_item1
|
||||
assert copied_q2.items.get() == copied_item2
|
||||
assert copied_q2.variations.get() == copied_item2.variations.get()
|
||||
|
||||
@@ -3336,6 +3336,33 @@ class CheckoutTestCase(BaseCheckoutTestCase, TestCase):
|
||||
assert 'Workshop 1' in response.content.decode()
|
||||
assert 'Workshop 2' in response.content.decode()
|
||||
|
||||
def test_set_addons_hidden_if_item_available(self):
|
||||
with scopes_disabled():
|
||||
self.workshopquota2 = Quota.objects.create(event=self.event, name='Workshop 1', size=5)
|
||||
self.workshopquota2.items.add(self.workshop2)
|
||||
self.workshopquota2.variations.add(self.workshop2a)
|
||||
self.workshop2.hidden_if_item_available = self.workshop1
|
||||
self.workshop2.save()
|
||||
|
||||
ItemAddOn.objects.create(base_item=self.ticket, addon_category=self.workshopcat, min_count=1)
|
||||
CartPosition.objects.create(
|
||||
event=self.event, cart_id=self.session_key, item=self.ticket,
|
||||
price=23, expires=now() - timedelta(minutes=10)
|
||||
)
|
||||
|
||||
response = self.client.get('/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), follow=True)
|
||||
self.assertRedirects(response, '/%s/%s/checkout/addons/' % (self.orga.slug, self.event.slug),
|
||||
target_status_code=200)
|
||||
assert 'Workshop 1' in response.content.decode()
|
||||
assert 'Workshop 2' not in response.content.decode()
|
||||
|
||||
self.workshopquota.size = 0
|
||||
self.workshopquota.save()
|
||||
|
||||
response = self.client.get('/%s/%s/checkout/addons/' % (self.orga.slug, self.event.slug), follow=True)
|
||||
assert 'Workshop 1' in response.content.decode()
|
||||
assert 'Workshop 2' in response.content.decode()
|
||||
|
||||
def test_set_addons_subevent(self):
|
||||
with scopes_disabled():
|
||||
self.event.has_subevents = True
|
||||
|
||||
@@ -631,6 +631,30 @@ class ItemDisplayTest(EventTestMixin, SoupTest):
|
||||
self.assertNotIn("SOLD OUT", doc.select("section:nth-of-type(1)")[0].text)
|
||||
self.assertIn("Late-bird", doc.select("section:nth-of-type(1)")[0].text)
|
||||
|
||||
def test_hidden_if_item_available(self):
|
||||
with scopes_disabled():
|
||||
q = Quota.objects.create(event=self.event, name='Early-bird', size=10)
|
||||
q2 = Quota.objects.create(event=self.event, name='Late-bird', size=10)
|
||||
item = Item.objects.create(event=self.event, name='Early-bird ticket', default_price=12)
|
||||
item2 = Item.objects.create(event=self.event, name='Late-bird ticket', default_price=12,
|
||||
hidden_if_item_available=item)
|
||||
q.items.add(item)
|
||||
q2.items.add(item2)
|
||||
self.event.settings.hide_sold_out = True
|
||||
|
||||
doc = self.get_doc('/%s/%s/' % (self.orga.slug, self.event.slug))
|
||||
self.assertIn("Early-bird", doc.select("section:nth-of-type(1)")[0].text)
|
||||
self.assertNotIn("SOLD OUT", doc.select("section:nth-of-type(1)")[0].text)
|
||||
self.assertNotIn("Late-bird", doc.select("section:nth-of-type(1)")[0].text)
|
||||
|
||||
q.size = 0
|
||||
q.save()
|
||||
|
||||
doc = self.get_doc('/%s/%s/' % (self.orga.slug, self.event.slug))
|
||||
self.assertNotIn("Early-bird", doc.select("section:nth-of-type(1)")[0].text)
|
||||
self.assertNotIn("SOLD OUT", doc.select("section:nth-of-type(1)")[0].text)
|
||||
self.assertIn("Late-bird", doc.select("section:nth-of-type(1)")[0].text)
|
||||
|
||||
def test_bundle_sold_out(self):
|
||||
with scopes_disabled():
|
||||
q = Quota.objects.create(event=self.event, name='Quota', size=2)
|
||||
|
||||
Reference in New Issue
Block a user