diff --git a/src/pretix/base/models/event.py b/src/pretix/base/models/event.py index a7dd8959a..c7d87ecb3 100644 --- a/src/pretix/base/models/event.py +++ b/src/pretix/base/models/event.py @@ -189,7 +189,9 @@ class EventMixin: ).order_by().values_list('quotas__pk').annotate( items=GroupConcat('pk', delimiter=',') ).values('items') - return qs.prefetch_related( + return qs.annotate( + has_paid_item=Exists(Item.objects.filter(event_id=OuterRef(cls._event_id), default_price__gt=0)) + ).prefetch_related( Prefetch( 'quotas', to_attr='active_quotas', @@ -280,6 +282,7 @@ class Event(EventMixin, LoggedModel): """ settings_namespace = 'event' + _event_id = 'pk' CURRENCY_CHOICES = [(c.alpha_3, c.alpha_3 + " - " + c.name) for c in settings.CURRENCIES] organizer = models.ForeignKey(Organizer, related_name="events", on_delete=models.PROTECT) testmode = models.BooleanField(default=False) @@ -786,7 +789,9 @@ class Event(EventMixin, LoggedModel): 'name_ascending': ('name', 'date_from'), 'name_descending': ('-name', 'date_from'), }[ordering] - subevs = queryset.filter( + subevs = queryset.annotate( + has_paid_item=self.cache.get_or_set('has_paid_item', lambda: self.items.filter(default_price__gt=0).exists(), 3600) + ).filter( Q(active=True) & Q(is_public=True) & ( Q(Q(date_to__isnull=True) & Q(date_from__gte=now() - timedelta(hours=24))) | Q(date_to__gte=now() - timedelta(hours=24)) @@ -980,6 +985,7 @@ class SubEvent(EventMixin, LoggedModel): :type location: str """ + _event_id = 'event_id' event = models.ForeignKey(Event, related_name="subevents", on_delete=models.PROTECT) active = models.BooleanField(default=False, verbose_name=_("Active"), help_text=_("Only with this checkbox enabled, this date is visible in the " diff --git a/src/pretix/presale/templates/pretixpresale/event/fragment_subevent_list.html b/src/pretix/presale/templates/pretixpresale/event/fragment_subevent_list.html index 7f553a1ab..57329b280 100644 --- a/src/pretix/presale/templates/pretixpresale/event/fragment_subevent_list.html +++ b/src/pretix/presale/templates/pretixpresale/event/fragment_subevent_list.html @@ -24,7 +24,11 @@ {% elif subev.best_availability_state == 20 %} {% trans "Reserved" %} {% elif subev.best_availability_state < 20 %} - {% trans "Sold out" %} + {% if subev.has_paid_item %} + {% trans "Sold out" %} + {% else %} + {% trans "Fully booked" %} + {% endif %} {% endif %} {% elif subev.presale_is_running %} {% trans "Book now" %} diff --git a/src/pretix/presale/templates/pretixpresale/fragment_calendar.html b/src/pretix/presale/templates/pretixpresale/fragment_calendar.html index 30d591d1c..437de1759 100644 --- a/src/pretix/presale/templates/pretixpresale/fragment_calendar.html +++ b/src/pretix/presale/templates/pretixpresale/fragment_calendar.html @@ -57,6 +57,11 @@ {% endif %} {{ event.time|date:"TIME_FORMAT" }} + {% if event.time_end %} + – {{ event.time_end|date:"TIME_FORMAT" }} + {% endif %} + {% if event.event.settings.show_date_to and event. %} + {% endif %} {% if not show_names|default_if_none:True %} {% endif %} @@ -74,7 +79,11 @@ {% elif event.event.best_availability_state == 20 %} {% trans "Reserved" %} {% elif event.event.best_availability_state < 20 %} - {% trans "Sold out" %} + {% if event.event.has_paid_item %} + {% trans "Sold out" %} + {% else %} + {% trans "Fully booked" %} + {% endif %} {% endif %} {% elif event.event.presale_is_running %} {% trans "Book now" %} diff --git a/src/pretix/presale/templates/pretixpresale/fragment_week_calendar.html b/src/pretix/presale/templates/pretixpresale/fragment_week_calendar.html index c10a5385b..732c6e6ec 100644 --- a/src/pretix/presale/templates/pretixpresale/fragment_week_calendar.html +++ b/src/pretix/presale/templates/pretixpresale/fragment_week_calendar.html @@ -58,7 +58,11 @@ {% elif event.event.best_availability_state == 20 %} {% trans "Reserved" %} {% elif event.event.best_availability_state < 20 %} - {% trans "Sold out" %} + {% if event.event.has_paid_item %} + {% trans "Sold out" %} + {% else %} + {% trans "Fully booked" %} + {% endif %} {% endif %} {% elif event.event.presale_is_running %} {% trans "Book now" %} diff --git a/src/pretix/presale/templates/pretixpresale/organizers/index.html b/src/pretix/presale/templates/pretixpresale/organizers/index.html index 47317a85e..855e968b9 100644 --- a/src/pretix/presale/templates/pretixpresale/organizers/index.html +++ b/src/pretix/presale/templates/pretixpresale/organizers/index.html @@ -108,7 +108,11 @@ {% elif e.best_availability_state == 20 %} {% trans "Reserved" %} {% elif e.best_availability_state < 20 %} - {% trans "Sold out" %} + {% if e.has_paid_item %} + {% trans "Sold out" %} + {% else %} + {% trans "Fully booked" %} + {% endif %} {% endif %} {% elif e.presale_is_running %} {% trans "Book now" %} diff --git a/src/pretix/presale/views/organizer.py b/src/pretix/presale/views/organizer.py index 56ab24dda..2824f0332 100644 --- a/src/pretix/presale/views/organizer.py +++ b/src/pretix/presale/views/organizer.py @@ -17,9 +17,7 @@ from django.views.generic import ListView, TemplateView from pytz import UTC from pretix.base.i18n import language -from pretix.base.models import ( - Event, EventMetaValue, SubEvent, SubEventMetaValue, -) +from pretix.base.models import Event, EventMetaValue, SubEvent, SubEventMetaValue from pretix.base.services.quotas import QuotaAvailability from pretix.helpers.daterange import daterange from pretix.helpers.formats.de.formats import WEEK_FORMAT diff --git a/src/pretix/presale/views/widget.py b/src/pretix/presale/views/widget.py index f9ef96ebe..48ae51b78 100644 --- a/src/pretix/presale/views/widget.py +++ b/src/pretix/presale/views/widget.py @@ -317,7 +317,10 @@ class WidgetAPIProductList(EventListMixin, View): availability['text'] = gettext('Reserved') elif ev.best_availability_state < Quota.AVAILABILITY_RESERVED: availability['color'] = 'red' - availability['text'] = gettext('Sold out') + if ev.has_paid_item: + availability['text'] = gettext('Sold out') + else: + availability['text'] = gettext('Fully booked') elif ev.presale_is_running: availability['color'] = 'green' availability['text'] = gettext('Book now')