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']
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
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
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
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())), ), ]
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
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)
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']
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
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
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() )
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
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)
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)
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
def static_choices(self): return [(sc.identifier, sc.verbose_name) for sc in get_all_sales_channels().values()]
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
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
def validate_sales_channel(self, channel): if channel not in get_all_sales_channels(): raise ValidationError('Unknown sales channel.') return channel
def validate_sales_channel(self, channel): if channel not in get_all_sales_channels(): raise ValidationError('Unknown sales channel.') return channel
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