Пример #1
0
    def __init__(self, **kwargs):
        self.event = kwargs.pop('event')
        kwargs.pop('locales', None)
        super().__init__(**kwargs)
        self.fields['limit_products'].queryset = self.event.items.all()
        self.fields['auto_checkin_sales_channels'] = forms.MultipleChoiceField(
            label=self.fields['auto_checkin_sales_channels'].label,
            help_text=self.fields['auto_checkin_sales_channels'].help_text,
            required=self.fields['auto_checkin_sales_channels'].required,
            choices=(
                (c.identifier, c.verbose_name) for c in get_all_sales_channels().values()
            ),
            widget=forms.CheckboxSelectMultiple
        )

        if not self.event.organizer.gates.exists():
            del self.fields['gates']
        else:
            self.fields['gates'].queryset = self.event.organizer.gates.all()

        if self.event.has_subevents:
            self.fields['subevent'].queryset = self.event.subevents.all()
            self.fields['subevent'].widget = Select2(
                attrs={
                    'data-model-select2': 'event',
                    'data-select2-url': reverse('control:event.subevents.select2', kwargs={
                        'event': self.event.slug,
                        'organizer': self.event.organizer.slug,
                    }),
                    'data-placeholder': pgettext_lazy('subevent', 'All dates')
                }
            )
            self.fields['subevent'].widget.choices = self.fields['subevent'].choices
        else:
            del self.fields['subevent']
Пример #2
0
    def validate(self, data):
        data = super().validate(data)
        event = self.context['event']

        full_data = self.to_internal_value(self.to_representation(self.instance)) if self.instance else {}
        full_data.update(data)

        for item in full_data.get('limit_products', []):
            if event != item.event:
                raise ValidationError(_('One or more items do not belong to this event.'))

        if event.has_subevents:
            if full_data.get('subevent') and event != full_data.get('subevent').event:
                raise ValidationError(_('The subevent does not belong to this event.'))
        else:
            if full_data.get('subevent'):
                raise ValidationError(_('The subevent does not belong to this event.'))

        for channel in full_data.get('auto_checkin_sales_channels') or []:
            if channel not in get_all_sales_channels():
                raise ValidationError(_('Unknown sales channel.'))

        CheckinList.validate_rules(data.get('rules'))

        return data
Пример #3
0
    def save(self, *args, **kwargs):
        if self.cleaned_data.get('copy_from'):
            self.instance.description = self.cleaned_data['copy_from'].description
            self.instance.active = self.cleaned_data['copy_from'].active
            self.instance.available_from = self.cleaned_data['copy_from'].available_from
            self.instance.available_until = self.cleaned_data['copy_from'].available_until
            self.instance.require_voucher = self.cleaned_data['copy_from'].require_voucher
            self.instance.hide_without_voucher = self.cleaned_data['copy_from'].hide_without_voucher
            self.instance.allow_cancel = self.cleaned_data['copy_from'].allow_cancel
            self.instance.min_per_order = self.cleaned_data['copy_from'].min_per_order
            self.instance.max_per_order = self.cleaned_data['copy_from'].max_per_order
            self.instance.checkin_attention = self.cleaned_data['copy_from'].checkin_attention
            self.instance.free_price = self.cleaned_data['copy_from'].free_price
            self.instance.original_price = self.cleaned_data['copy_from'].original_price
            self.instance.sales_channels = self.cleaned_data['copy_from'].sales_channels
        else:
            # Add to all sales channels by default
            self.instance.sales_channels = [k for k in get_all_sales_channels().keys()]

        self.instance.position = (self.event.items.aggregate(p=Max('position'))['p'] or 0) + 1
        instance = super().save(*args, **kwargs)

        if not self.event.has_subevents and not self.cleaned_data.get('has_variations'):
            if self.cleaned_data.get('quota_option') == self.EXISTING and self.cleaned_data.get('quota_add_existing') is not None:
                quota = self.cleaned_data.get('quota_add_existing')
                quota.items.add(self.instance)
                quota.log_action('pretix.event.quota.changed', user=self.user, data={
                    'item_added': self.instance.pk
                })
            elif self.cleaned_data.get('quota_option') == self.NEW:
                quota_name = self.cleaned_data.get('quota_add_new_name')
                quota_size = self.cleaned_data.get('quota_add_new_size')

                quota = Quota.objects.create(
                    event=self.event, name=quota_name, size=quota_size
                )
                quota.items.add(self.instance)
                quota.log_action('pretix.event.quota.added', user=self.user, data={
                    'name': quota_name,
                    'size': quota_size,
                    'items': [self.instance.pk]
                })

        if self.cleaned_data.get('has_variations'):
            if self.cleaned_data.get('copy_from') and self.cleaned_data.get('copy_from').has_variations:
                for variation in self.cleaned_data['copy_from'].variations.all():
                    ItemVariation.objects.create(item=instance, value=variation.value, active=variation.active,
                                                 position=variation.position, default_price=variation.default_price)
            else:
                ItemVariation.objects.create(
                    item=instance, value=__('Standard')
                )

        if self.cleaned_data.get('copy_from'):
            for question in self.cleaned_data['copy_from'].questions.all():
                question.items.add(instance)

            item_copy_data.send(sender=self.event, source=self.cleaned_data['copy_from'], target=instance)

        return instance
Пример #4
0
 def __init__(self, *args, **kwargs):
     super().__init__(*args, **kwargs)
     self.fields['category'].queryset = self.instance.event.categories.all()
     self.fields['tax_rule'].queryset = self.instance.event.tax_rules.all()
     self.fields['description'].widget.attrs['placeholder'] = _(
         'e.g. This reduced price is available for full-time students, jobless and people '
         'over 65. This ticket includes access to all parts of the event, except the VIP '
         'area.')
     if self.event.tax_rules.exists():
         self.fields['tax_rule'].required = True
     self.fields['description'].widget.attrs['rows'] = '4'
     self.fields['sales_channels'] = forms.MultipleChoiceField(
         label=_('Sales channels'),
         choices=((c.identifier, c.verbose_name)
                  for c in get_all_sales_channels().values()),
         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(
         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':
             _('Quota')
         })
     self.fields['hidden_if_available'].widget.choices = self.fields[
         'hidden_if_available'].choices
     self.fields['hidden_if_available'].required = False
Пример #5
0
    def get_context_data(self, **kwargs):
        ctx = super().get_context_data(**kwargs)
        clists = list(ctx['checkinlists'])
        sales_channels = get_all_sales_channels()

        for cl in clists:
            if cl.subevent:
                cl.subevent.event = self.request.event  # re-use same event object to make sure settings are cached
            cl.auto_checkin_sales_channels = [sales_channels[channel] for channel in cl.auto_checkin_sales_channels]
        ctx['checkinlists'] = clists

        return ctx
class Migration(migrations.Migration):
    dependencies = [
        ('pretixbase', '0171_auto_20201126_1635'),
    ]

    operations = [
        migrations.AddField(
            model_name='event',
            name='sales_channels',
            field=pretix.base.models.fields.MultiStringField(
                default=list(get_all_sales_channels().keys())),
        ),
    ]
Пример #7
0
    def validate_testmode(self, testmode):
        if 'sales_channel' in self.initial_data:
            try:
                sales_channel = get_all_sales_channels()[self.initial_data['sales_channel']]

                if testmode and not sales_channel.testmode_supported:
                    raise ValidationError('This sales channel does not provide support for testmode.')
            except KeyError:
                # We do not need to raise a ValidationError here, since there is another check to validate the
                # sales_channel
                pass

        return testmode
Пример #8
0
 def __init__(self, *args, **kwargs):
     super().__init__(*args, **kwargs)
     self.fields['category'].queryset = self.instance.event.categories.all()
     self.fields['tax_rule'].queryset = self.instance.event.tax_rules.all()
     self.fields['description'].widget.attrs['placeholder'] = _(
         'e.g. This reduced price is available for full-time students, jobless and people '
         'over 65. This ticket includes access to all parts of the event, except the VIP '
         'area.')
     self.fields['sales_channels'] = forms.MultipleChoiceField(
         label=_('Sales channels'),
         choices=((c.identifier, c.verbose_name)
                  for c in get_all_sales_channels().values()),
         widget=forms.CheckboxSelectMultiple)
     change_decimal_field(self.fields['default_price'], self.event.currency)
Пример #9
0
    def __init__(self, *args, **kwargs):
        qs = kwargs.pop('membership_types')
        super().__init__(*args, **kwargs)
        change_decimal_field(self.fields['default_price'], self.event.currency)
        self.fields['sales_channels'] = forms.MultipleChoiceField(
            label=_('Sales channels'),
            required=False,
            choices=(
                (c.identifier, c.verbose_name) for c in get_all_sales_channels().values()
            ),
            help_text=_('The sales channel selection for the product as a whole takes precedence, so if a sales channel is '
                        'selected here but not on product level, the variation will not be available.'),
            widget=forms.CheckboxSelectMultiple
        )
        if not self.instance.pk:
            self.initial.setdefault('sales_channels', list(get_all_sales_channels().keys()))

        self.fields['description'].widget.attrs['rows'] = 3
        if qs:
            self.fields['require_membership_types'].queryset = qs
        else:
            del self.fields['require_membership']
            del self.fields['require_membership_types']
Пример #10
0
def control_item_forms(sender, request, item, **kwargs):
    forms = []
    for k, v in sorted(list(get_all_sales_channels().items()), key=lambda a: (int(a[0] != 'web'), a[0])):
        try:
            inst = TicketLayoutItem.objects.get(item=item, sales_channel=k)
        except TicketLayoutItem.DoesNotExist:
            inst = TicketLayoutItem(item=item)
        forms.append(TicketLayoutItemForm(
            instance=inst,
            event=sender,
            sales_channel=v,
            data=(request.POST if request.method == "POST" else None),
            prefix="ticketlayoutitem_{}".format(k)
        ))
    return forms
Пример #11
0
def control_item_forms(sender, request, item, **kwargs):
    forms = []
    for k, v in sorted(list(get_all_sales_channels().items()), key=lambda a: (int(a[0] != 'web'), a[0])):
        try:
            inst = TicketLayoutItem.objects.get(item=item, sales_channel=k)
        except TicketLayoutItem.DoesNotExist:
            inst = TicketLayoutItem(item=item)
        forms.append(TicketLayoutItemForm(
            instance=inst,
            event=sender,
            sales_channel=v,
            data=(request.POST if request.method == "POST" else None),
            prefix="ticketlayoutitem_{}".format(k)
        ))
    return forms
Пример #12
0
 def __init__(self, *args, **kwargs):
     event = kwargs.get('obj')
     super().__init__(*args, **kwargs)
     self.fields['invoice_renderer'].choices = [
         (r.identifier, r.verbose_name) for r in event.get_invoice_renderers().values()
     ]
     self.fields['invoice_numbers_prefix'].widget.attrs['placeholder'] = event.slug.upper() + '-'
     if event.settings.invoice_numbers_prefix:
         self.fields['invoice_numbers_prefix_cancellations'].widget.attrs['placeholder'] = event.settings.invoice_numbers_prefix
     else:
         self.fields['invoice_numbers_prefix_cancellations'].widget.attrs['placeholder'] = event.slug.upper() + '-'
     locale_names = dict(settings.LANGUAGES)
     self.fields['invoice_language'].choices = [('__user__', _('The user\'s language'))] + [(a, locale_names[a]) for a in event.settings.locales]
     self.fields['invoice_generate_sales_channels'].choices = (
         (c.identifier, c.verbose_name) for c in get_all_sales_channels().values()
     )
Пример #13
0
    def get_context_data(self, **kwargs):
        ctx = super().get_context_data(**kwargs)
        clists = list(ctx['checkinlists'])
        sales_channels = get_all_sales_channels()

        for cl in clists:
            if cl.subevent:
                cl.subevent.event = self.request.event  # re-use same event object to make sure settings are cached
            cl.auto_checkin_sales_channels = [sales_channels[channel] for channel in cl.auto_checkin_sales_channels]
        ctx['checkinlists'] = clists

        ctx['can_change_organizer_settings'] = self.request.user.has_organizer_permission(
            self.request.organizer,
            'can_change_organizer_settings',
            self.request
        )

        return ctx
Пример #14
0
 def __init__(self, *args, **kwargs):
     super().__init__(*args, **kwargs)
     self.fields['category'].queryset = self.instance.event.categories.all()
     self.fields['tax_rule'].queryset = self.instance.event.tax_rules.all()
     self.fields['description'].widget.attrs['placeholder'] = _(
         'e.g. This reduced price is available for full-time students, jobless and people '
         'over 65. This ticket includes access to all parts of the event, except the VIP '
         'area.'
     )
     if self.event.tax_rules.exists():
         self.fields['tax_rule'].required = True
     self.fields['description'].widget.attrs['rows'] = '4'
     self.fields['sales_channels'] = forms.MultipleChoiceField(
         label=_('Sales channels'),
         choices=(
             (c.identifier, c.verbose_name) for c in get_all_sales_channels().values()
         ),
         widget=forms.CheckboxSelectMultiple
     )
     change_decimal_field(self.fields['default_price'], self.event.currency)
Пример #15
0
def set_sales_channels(apps, schema_editor):
    # We now allow restricting some mails to certain sales channels
    # The default is changing from all channels to "web" only
    # Therefore, for existing events, we enable all sales channels
    Event_SettingsStore = apps.get_model('pretixbase', 'Event_SettingsStore')
    Event = apps.get_model('pretixbase', 'Event')
    all_sales_channels = "[" + ", ".join(
        '"' + sc + '"' for sc in get_all_sales_channels()) + "]"
    batch_size = 1000
    Event_SettingsStore.objects.bulk_create([
        Event_SettingsStore(object=event,
                            key="mail_sales_channel_placed_paid",
                            value=all_sales_channels)
        for event in Event.objects.all()
    ],
                                            batch_size=batch_size)
    Event_SettingsStore.objects.bulk_create([
        Event_SettingsStore(object=event,
                            key="mail_sales_channel_download_reminder",
                            value=all_sales_channels)
        for event in Event.objects.all()
    ],
                                            batch_size=batch_size)
Пример #16
0
 def clean(self, value, previous_values):
     if not value:
         value = 'web'
     if value not in get_all_sales_channels():
         raise ValidationError(_("Please enter a valid sales channel."))
     return value
Пример #17
0
 def static_choices(self):
     return [(sc.identifier, sc.verbose_name)
             for sc in get_all_sales_channels().values()]
Пример #18
0
    def save(self, *args, **kwargs):
        if self.cleaned_data.get('copy_from'):
            self.instance.description = self.cleaned_data['copy_from'].description
            self.instance.active = self.cleaned_data['copy_from'].active
            self.instance.available_from = self.cleaned_data['copy_from'].available_from
            self.instance.available_until = self.cleaned_data['copy_from'].available_until
            self.instance.require_voucher = self.cleaned_data['copy_from'].require_voucher
            self.instance.hide_without_voucher = self.cleaned_data['copy_from'].hide_without_voucher
            self.instance.allow_cancel = self.cleaned_data['copy_from'].allow_cancel
            self.instance.min_per_order = self.cleaned_data['copy_from'].min_per_order
            self.instance.max_per_order = self.cleaned_data['copy_from'].max_per_order
            self.instance.checkin_attention = self.cleaned_data['copy_from'].checkin_attention
            self.instance.free_price = self.cleaned_data['copy_from'].free_price
            self.instance.original_price = self.cleaned_data['copy_from'].original_price
            self.instance.sales_channels = self.cleaned_data['copy_from'].sales_channels
        else:
            # Add to all sales channels by default
            self.instance.sales_channels = [k for k in get_all_sales_channels().keys()]

        self.instance.position = (self.event.items.aggregate(p=Max('position'))['p'] or 0) + 1
        instance = super().save(*args, **kwargs)

        if not self.event.has_subevents and not self.cleaned_data.get('has_variations'):
            if self.cleaned_data.get('quota_option') == self.EXISTING and self.cleaned_data.get('quota_add_existing') is not None:
                quota = self.cleaned_data.get('quota_add_existing')
                quota.items.add(self.instance)
                quota.log_action('pretix.event.quota.changed', user=self.user, data={
                    'item_added': self.instance.pk
                })
            elif self.cleaned_data.get('quota_option') == self.NEW:
                quota_name = self.cleaned_data.get('quota_add_new_name')
                quota_size = self.cleaned_data.get('quota_add_new_size')

                quota = Quota.objects.create(
                    event=self.event, name=quota_name, size=quota_size
                )
                quota.items.add(self.instance)
                quota.log_action('pretix.event.quota.added', user=self.user, data={
                    'name': quota_name,
                    'size': quota_size,
                    'items': [self.instance.pk]
                })

        if self.cleaned_data.get('has_variations'):
            if self.cleaned_data.get('copy_from') and self.cleaned_data.get('copy_from').has_variations:
                for variation in self.cleaned_data['copy_from'].variations.all():
                    ItemVariation.objects.create(item=instance, value=variation.value, active=variation.active,
                                                 position=variation.position, default_price=variation.default_price)
            else:
                ItemVariation.objects.create(
                    item=instance, value=__('Standard')
                )

        if self.cleaned_data.get('copy_from'):
            for question in self.cleaned_data['copy_from'].questions.all():
                question.items.add(instance)
            for a in self.cleaned_data['copy_from'].addons.all():
                instance.addons.create(addon_category=a.addon_category, min_count=a.min_count, max_count=a.max_count,
                                       price_included=a.price_included, position=a.position)
            for b in self.cleaned_data['copy_from'].bundles.all():
                instance.bundles.create(bundled_item=b.bundled_item, bundled_variation=b.bundled_variation,
                                        count=b.count, designated_price=b.designated_price)

            item_copy_data.send(sender=self.event, source=self.cleaned_data['copy_from'], target=instance)

        return instance
Пример #19
0
def get_grouped_items(event,
                      subevent=None,
                      voucher=None,
                      channel='web',
                      require_seat=0,
                      base_qs=None,
                      allow_addons=False,
                      quota_cache=None,
                      filter_items=None,
                      filter_categories=None):
    base_qs = base_qs if base_qs is not None else event.items
    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('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(
                                     variation_id=OuterRef('pk'),
                                     subevent=subevent,
                                     disabled=True,
                                 )), ).filter(active=True,
                                              quotas__isnull=False,
                                              subevent_disabled=False).
                     prefetch_related(
                         Prefetch('quotas',
                                  to_attr='_subevent_quotas',
                                  queryset=event.quotas.using(
                                      settings.DATABASE_REPLICA).filter(
                                          subevent=subevent))).distinct()),
        ).annotate(
            quotac=Count('quotas'),
            has_variations=Count('variations'),
            subevent_disabled=Exists(
                SubEventItem.objects.filter(
                    item_id=OuterRef('pk'),
                    subevent=subevent,
                    disabled=True,
                )),
            requires_seat=Exists(
                SeatCategoryMapping.objects.filter(product_id=OuterRef('pk'),
                                                   subevent=subevent)),
        ).filter(
            quotac__gt=0,
            subevent_disabled=False,
        ).order_by('category__position', 'category_id', 'position', 'name')
    if require_seat:
        items = items.filter(requires_seat__gt=0)
    else:
        items = items.filter(requires_seat=0)

    if filter_items:
        items = items.filter(pk__in=[a for a in filter_items if a.isdigit()])
    if filter_categories:
        items = items.filter(
            category_id__in=[a for a in filter_categories if a.isdigit()])

    display_add_to_cart = False
    external_quota_cache = quota_cache or event.cache.get('item_quota_cache')
    quota_cache = external_quota_cache or {}

    if subevent:
        item_price_override = subevent.item_price_overrides
        var_price_override = subevent.var_price_overrides
    else:
        item_price_override = {}
        var_price_override = {}

    restrict_vars = set()
    if voucher and voucher.quota_id:
        # If a voucher is set to a specific quota, we need to filter out on that level
        restrict_vars = set(voucher.quota.variations.all())

    quotas_to_compute = []
    for item in items:
        if item.has_variations:
            for v in item.available_variations:
                for q in v._subevent_quotas:
                    if q not in quota_cache:
                        quotas_to_compute.append(q)
        else:
            for q in item._subevent_quotas:
                if q not in quota_cache:
                    quotas_to_compute.append(q)

    if quotas_to_compute:
        qa = QuotaAvailability()
        qa.queue(*quotas_to_compute)
        qa.compute()
        quota_cache.update({q.pk: r for q, r in qa.results.items()})

    for item in items:
        if voucher and voucher.item_id and voucher.variation_id:
            # Restrict variations if the voucher only allows one
            item.available_variations = [
                v for v in item.available_variations
                if v.pk == voucher.variation_id
            ]

        if get_all_sales_channels()[channel].unlimited_items_per_order:
            max_per_order = sys.maxsize
        else:
            max_per_order = item.max_per_order or int(
                event.settings.max_items_per_order)

        if item.hidden_if_available:
            q = item.hidden_if_available.availability(_cache=quota_cache)
            if q[0] == Quota.AVAILABILITY_OK:
                item._remove = True
                continue

        item.description = str(item.description)
        for recv, resp in item_description.send(sender=event,
                                                item=item,
                                                variation=None):
            if resp:
                item.description += ("<br/>"
                                     if item.description else "") + resp

        if not item.has_variations:
            item._remove = False
            if not bool(item._subevent_quotas):
                item._remove = True
                continue

            if voucher and (voucher.allow_ignore_quota or voucher.block_quota):
                item.cached_availability = (Quota.AVAILABILITY_OK,
                                            voucher.max_usages -
                                            voucher.redeemed)
            else:
                item.cached_availability = list(
                    item.check_quotas(subevent=subevent,
                                      _cache=quota_cache,
                                      include_bundled=True))

            if event.settings.hide_sold_out and item.cached_availability[
                    0] < Quota.AVAILABILITY_RESERVED:
                item._remove = True
                continue

            item.order_max = min(
                item.cached_availability[1] if item.cached_availability[1]
                is not None else sys.maxsize, max_per_order)

            original_price = item_price_override.get(item.pk,
                                                     item.default_price)
            if voucher:
                price = voucher.calculate_price(original_price)
            else:
                price = original_price

            item.display_price = item.tax(price,
                                          currency=event.currency,
                                          include_bundled=True)

            if price != original_price:
                item.original_price = item.tax(original_price,
                                               currency=event.currency,
                                               include_bundled=True)
            else:
                item.original_price = (
                    item.tax(item.original_price,
                             currency=event.currency,
                             include_bundled=True,
                             base_price_is='net'
                             if event.settings.display_net_prices else
                             'gross')  # backwards-compat
                    if item.original_price else None)

            display_add_to_cart = display_add_to_cart or item.order_max > 0
        else:
            for var in item.available_variations:
                var.description = str(var.description)
                for recv, resp in item_description.send(sender=event,
                                                        item=item,
                                                        variation=var):
                    if resp:
                        var.description += ("<br/>"
                                            if var.description else "") + resp

                if voucher and (voucher.allow_ignore_quota
                                or voucher.block_quota):
                    var.cached_availability = (Quota.AVAILABILITY_OK,
                                               voucher.max_usages -
                                               voucher.redeemed)
                else:
                    var.cached_availability = list(
                        var.check_quotas(subevent=subevent,
                                         _cache=quota_cache,
                                         include_bundled=True))

                var.order_max = min(
                    var.cached_availability[1] if var.cached_availability[1]
                    is not None else sys.maxsize, max_per_order)

                original_price = var_price_override.get(var.pk, var.price)
                if voucher:
                    price = voucher.calculate_price(original_price)
                else:
                    price = original_price

                var.display_price = var.tax(price,
                                            currency=event.currency,
                                            include_bundled=True)

                if price != original_price:
                    var.original_price = var.tax(original_price,
                                                 currency=event.currency,
                                                 include_bundled=True)
                else:
                    var.original_price = (
                        var.tax(var.original_price or item.original_price,
                                currency=event.currency,
                                include_bundled=True,
                                base_price_is='net'
                                if event.settings.display_net_prices else
                                'gross')  # backwards-compat
                    ) if var.original_price or item.original_price else None

                display_add_to_cart = display_add_to_cart or var.order_max > 0

            item.original_price = (
                item.tax(
                    item.original_price,
                    currency=event.currency,
                    include_bundled=True,
                    base_price_is='net' if event.settings.display_net_prices
                    else 'gross')  # backwards-compat
                if item.original_price else None)

            item.available_variations = [
                v for v in item.available_variations
                if v._subevent_quotas and (
                    not voucher or not voucher.quota_id or v in restrict_vars)
            ]

            if event.settings.hide_sold_out:
                item.available_variations = [
                    v for v in item.available_variations
                    if v.cached_availability[0] >= Quota.AVAILABILITY_RESERVED
                ]

            if voucher and voucher.variation_id:
                item.available_variations = [
                    v for v in item.available_variations
                    if v.pk == voucher.variation_id
                ]

            if len(item.available_variations) > 0:
                item.min_price = min([
                    v.display_price.net if event.settings.display_net_prices
                    else v.display_price.gross
                    for v in item.available_variations
                ])
                item.max_price = max([
                    v.display_price.net if event.settings.display_net_prices
                    else v.display_price.gross
                    for v in item.available_variations
                ])

            item._remove = not bool(item.available_variations)

    if not external_quota_cache and not voucher and not allow_addons:
        event.cache.set('item_quota_cache', quota_cache, 5)
    items = [
        item for item in items
        if (len(item.available_variations) > 0 or not item.has_variations)
        and not item._remove
    ]
    return items, display_add_to_cart
Пример #20
0
 def validate_sales_channel(self, channel):
     if channel not in get_all_sales_channels():
         raise ValidationError('Unknown sales channel.')
     return channel
Пример #21
0
 def validate_sales_channel(self, channel):
     if channel not in get_all_sales_channels():
         raise ValidationError('Unknown sales channel.')
     return channel
Пример #22
0
    def save(self, *args, **kwargs):
        if self.cleaned_data.get('copy_from'):
            src = self.cleaned_data['copy_from']
            fields = (
                'description',
                'active',
                'available_from',
                'available_until',
                'require_voucher',
                'hide_without_voucher',
                'allow_cancel',
                'min_per_order',
                'max_per_order',
                'generate_tickets',
                'checkin_attention',
                'free_price',
                'original_price',
                'sales_channels',
                'issue_giftcard',
                'require_approval',
                'allow_waitinglist',
                'show_quota_left',
                'hidden_if_available',
                'require_bundling',
                'checkin_attention',
                'require_membership',
                'grant_membership_type',
                'grant_membership_duration_like_event',
                'grant_membership_duration_days',
                'grant_membership_duration_months',
            )
            for f in fields:
                setattr(self.instance, f, getattr(src, f))

            if src.picture:
                self.instance.picture.save(os.path.basename(src.picture.name), src.picture)
        else:
            # Add to all sales channels by default
            self.instance.sales_channels = list(get_all_sales_channels().keys())

        self.instance.position = (self.event.items.aggregate(p=Max('position'))['p'] or 0) + 1
        instance = super().save(*args, **kwargs)

        if not self.event.has_subevents and not self.cleaned_data.get('has_variations'):
            if self.cleaned_data.get('quota_option') == self.EXISTING and self.cleaned_data.get('quota_add_existing') is not None:
                quota = self.cleaned_data.get('quota_add_existing')
                quota.items.add(self.instance)
                quota.log_action('pretix.event.quota.changed', user=self.user, data={
                    'item_added': self.instance.pk
                })
            elif self.cleaned_data.get('quota_option') == self.NEW:
                quota_name = self.cleaned_data.get('quota_add_new_name')
                quota_size = self.cleaned_data.get('quota_add_new_size')

                quota = Quota.objects.create(
                    event=self.event, name=quota_name, size=quota_size
                )
                quota.items.add(self.instance)
                quota.log_action('pretix.event.quota.added', user=self.user, data={
                    'name': quota_name,
                    'size': quota_size,
                    'items': [self.instance.pk]
                })

        if self.cleaned_data.get('copy_from'):
            self.instance.require_membership_types.set(
                self.cleaned_data['copy_from'].require_membership_types.all()
            )
        if self.cleaned_data.get('has_variations'):
            if self.cleaned_data.get('copy_from') and self.cleaned_data.get('copy_from').has_variations:
                for variation in self.cleaned_data['copy_from'].variations.all():
                    ItemVariation.objects.create(item=instance, value=variation.value, active=variation.active,
                                                 position=variation.position, default_price=variation.default_price,
                                                 description=variation.description, original_price=variation.original_price)
            else:
                ItemVariation.objects.create(
                    item=instance, value=__('Standard')
                )

        if self.cleaned_data.get('copy_from'):
            for question in self.cleaned_data['copy_from'].questions.all():
                question.items.add(instance)
            for a in self.cleaned_data['copy_from'].addons.all():
                instance.addons.create(addon_category=a.addon_category, min_count=a.min_count, max_count=a.max_count,
                                       price_included=a.price_included, position=a.position,
                                       multi_allowed=a.multi_allowed)
            for b in self.cleaned_data['copy_from'].bundles.all():
                instance.bundles.create(bundled_item=b.bundled_item, bundled_variation=b.bundled_variation,
                                        count=b.count, designated_price=b.designated_price)

            item_copy_data.send(sender=self.event, source=self.cleaned_data['copy_from'], target=instance)

        return instance