mirror of
https://github.com/pretix/pretix.git
synced 2026-05-05 15:14:04 +00:00
New implementation of sales channels (#4111)
Co-authored-by: Martin Gross <gross@rami.io>
This commit is contained in:
@@ -1162,7 +1162,7 @@ class StripeCC(StripeMethod):
|
||||
request.sales_channel.identifier == 'resellers'
|
||||
|
||||
if payment:
|
||||
return moto and payment.order.sales_channel == 'resellers'
|
||||
return moto and payment.order.sales_channel.identifier == 'resellers'
|
||||
|
||||
return moto
|
||||
|
||||
|
||||
@@ -21,31 +21,50 @@
|
||||
#
|
||||
from django.conf import settings
|
||||
from django.db import transaction
|
||||
from rest_framework import viewsets
|
||||
from django.db.models import QuerySet
|
||||
from django.utils.functional import lazy
|
||||
from rest_framework import serializers, viewsets
|
||||
from rest_framework.exceptions import ValidationError
|
||||
|
||||
from pretix.api.serializers.i18n import I18nAwareModelSerializer
|
||||
from pretix.api.serializers.order import CompatibleJSONField
|
||||
|
||||
from ...api.serializers.fields import UploadedFileField
|
||||
from ...base.models import SalesChannel
|
||||
from ...base.pdf import PdfLayoutValidator
|
||||
from ...multidomain.utils import static_absolute
|
||||
from .models import TicketLayout, TicketLayoutItem
|
||||
|
||||
|
||||
class ItemAssignmentSerializer(I18nAwareModelSerializer):
|
||||
sales_channel = serializers.SlugRelatedField(
|
||||
slug_field='identifier',
|
||||
queryset=SalesChannel.objects.none(),
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = TicketLayoutItem
|
||||
fields = ('id', 'layout', 'item', 'sales_channel')
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.fields["sales_channel"].queryset = self.context["event"].organizer.sales_channels.all()
|
||||
|
||||
|
||||
class NestedItemAssignmentSerializer(I18nAwareModelSerializer):
|
||||
sales_channel = serializers.SlugRelatedField(
|
||||
slug_field='identifier',
|
||||
queryset=SalesChannel.objects.none(),
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = TicketLayoutItem
|
||||
fields = ('item', 'sales_channel')
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.fields["sales_channel"].queryset = lazy(lambda: self.context["event"].organizer.sales_channels.all(), QuerySet)
|
||||
|
||||
|
||||
class TicketLayoutSerializer(I18nAwareModelSerializer):
|
||||
layout = CompatibleJSONField(
|
||||
@@ -131,3 +150,9 @@ class TicketLayoutItemViewSet(viewsets.ReadOnlyModelViewSet):
|
||||
|
||||
def get_queryset(self):
|
||||
return TicketLayoutItem.objects.filter(item__event=self.request.event)
|
||||
|
||||
def get_serializer_context(self):
|
||||
return {
|
||||
**super().get_serializer_context(),
|
||||
'event': self.request.event,
|
||||
}
|
||||
|
||||
@@ -223,7 +223,7 @@ class AllTicketsPDF(BaseExporter):
|
||||
|
||||
with language(op.order.locale, o.event.settings.region):
|
||||
layout = o.layout_map.get(
|
||||
(op.item_id, op.order.sales_channel),
|
||||
(op.item_id, op.order.sales_channel_id),
|
||||
o.layout_map.get(
|
||||
(op.item_id, 'web'),
|
||||
o.default_layout
|
||||
|
||||
@@ -47,7 +47,7 @@ class TicketLayoutItemForm(forms.ModelForm):
|
||||
super().__init__(*args, **kwargs)
|
||||
if self.sales_channel.identifier != 'web':
|
||||
self.fields['layout'].label = _('PDF ticket layout for {channel}').format(
|
||||
channel=self.sales_channel.verbose_name
|
||||
channel=self.sales_channel.label
|
||||
)
|
||||
self.fields['layout'].empty_label = _('(Same as PDF ticket layout)')
|
||||
else:
|
||||
@@ -57,7 +57,7 @@ class TicketLayoutItemForm(forms.ModelForm):
|
||||
self.fields['layout'].required = False
|
||||
|
||||
def save(self, commit=True):
|
||||
self.instance.sales_channel = self.sales_channel.identifier
|
||||
self.instance.sales_channel = self.sales_channel
|
||||
if self.cleaned_data['layout'] is None:
|
||||
if self.instance.pk:
|
||||
self.instance.delete()
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
# Generated by Django 4.2.10 on 2024-05-14 10:19
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("pretixbase", "0267_remove_old_sales_channels"),
|
||||
("ticketoutputpdf", "0008_bigint"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RenameField(
|
||||
model_name="ticketlayoutitem",
|
||||
old_name="sales_channel",
|
||||
new_name="sales_channel_type",
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name="ticketlayoutitem",
|
||||
unique_together=set(),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="ticketlayoutitem",
|
||||
name="sales_channel",
|
||||
field=models.ForeignKey(
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
to="pretixbase.saleschannel",
|
||||
),
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name="ticketlayoutitem",
|
||||
unique_together={("item", "layout", "sales_channel")},
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,22 @@
|
||||
# Generated by Django 4.2.10 on 2024-05-14 10:30
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
def use_sales_channels(apps, schema_editor):
|
||||
SalesChannel = apps.get_model("pretixbase", "SalesChannel")
|
||||
TicketLayoutItem = apps.get_model("ticketoutputpdf", "TicketLayoutItem")
|
||||
for sc in SalesChannel.objects.all():
|
||||
TicketLayoutItem.objects.filter(item__event__organizer_id=sc.organizer_id, sales_channel_type=sc.identifier).update(
|
||||
sales_channel=sc
|
||||
)
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("ticketoutputpdf", "0009_sales_channels_new_fields"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(use_sales_channels, migrations.RunPython.noop),
|
||||
]
|
||||
@@ -0,0 +1,27 @@
|
||||
# Generated by Django 4.2.10 on 2024-05-14 10:31
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("pretixbase", "0267_remove_old_sales_channels"),
|
||||
("ticketoutputpdf", "0010_sales_channels_migrate_data"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name="ticketlayoutitem",
|
||||
name="sales_channel_type",
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="ticketlayoutitem",
|
||||
name="sales_channel",
|
||||
field=models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
to="pretixbase.saleschannel",
|
||||
),
|
||||
),
|
||||
]
|
||||
@@ -254,7 +254,10 @@ class TicketLayoutItem(models.Model):
|
||||
item = models.ForeignKey('pretixbase.Item', null=True, blank=True, related_name='ticketlayout_assignments',
|
||||
on_delete=models.CASCADE)
|
||||
layout = models.ForeignKey('TicketLayout', on_delete=models.CASCADE, related_name='item_assignments')
|
||||
sales_channel = models.CharField(max_length=190, default='web')
|
||||
sales_channel = models.ForeignKey(
|
||||
"pretixbase.SalesChannel",
|
||||
on_delete=models.CASCADE,
|
||||
)
|
||||
|
||||
class Meta:
|
||||
unique_together = (('item', 'layout', 'sales_channel'),)
|
||||
|
||||
@@ -28,8 +28,7 @@ from django.urls import reverse
|
||||
from django.utils.html import escape
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from pretix.base.channels import get_all_sales_channels
|
||||
from pretix.base.models import Event
|
||||
from pretix.base.models import Event, SalesChannel
|
||||
from pretix.base.signals import ( # NOQA: legacy import
|
||||
EventPluginSignal, event_copy_data, item_copy_data, layout_text_variables,
|
||||
logentry_display, logentry_object_link, register_data_exporters,
|
||||
@@ -67,25 +66,25 @@ def register_multievent_data(sender, **kwargs):
|
||||
def control_item_forms(sender, request, item, **kwargs):
|
||||
forms = []
|
||||
queryset = sender.ticket_layouts.all()
|
||||
for k, v in sorted(list(get_all_sales_channels().items()), key=lambda a: (int(a[0] != 'web'), a[0])):
|
||||
for sc in sorted(list(sender.organizer.sales_channels.all()), key=lambda a: (int(a.identifier != 'web'), a.identifier)):
|
||||
try:
|
||||
inst = TicketLayoutItem.objects.get(item=item, sales_channel=k)
|
||||
inst = TicketLayoutItem.objects.get(item=item, sales_channel=sc)
|
||||
except TicketLayoutItem.DoesNotExist:
|
||||
inst = TicketLayoutItem(item=item)
|
||||
forms.append(TicketLayoutItemForm(
|
||||
instance=inst,
|
||||
event=sender,
|
||||
sales_channel=v,
|
||||
sales_channel=sc,
|
||||
queryset=queryset,
|
||||
data=(request.POST if request.method == "POST" else None),
|
||||
prefix="ticketlayoutitem_{}".format(k)
|
||||
prefix="ticketlayoutitem_{}".format(sc.identifier)
|
||||
))
|
||||
return forms
|
||||
|
||||
|
||||
@receiver(item_copy_data, dispatch_uid="pretix_ticketoutputpdf_item_copy")
|
||||
def copy_item(sender, source, target, **kwargs):
|
||||
for tli in TicketLayoutItem.objects.filter(item=source):
|
||||
for tli in TicketLayoutItem.objects.filter(item=source).select_related("sales_channel"):
|
||||
TicketLayoutItem.objects.create(item=target, layout=tli.layout, sales_channel=tli.sales_channel)
|
||||
|
||||
|
||||
@@ -122,9 +121,16 @@ def pdf_event_copy_data_receiver(sender, other, item_map, question_map, **kwargs
|
||||
|
||||
layout_map[oldid] = bl
|
||||
|
||||
for bi in TicketLayoutItem.objects.filter(item__event=other):
|
||||
for bi in TicketLayoutItem.objects.filter(item__event=other).select_related("sales_channel"):
|
||||
if sender.organizer != other.organizer:
|
||||
try:
|
||||
sc = sender.organizer.sales_channels.get(identifier=bi.sales_channel.identifier)
|
||||
except SalesChannel.DoesNotExist:
|
||||
continue
|
||||
else:
|
||||
sc = bi.sales_channel
|
||||
TicketLayoutItem.objects.create(item=item_map.get(bi.item_id), layout=layout_map.get(bi.layout_id),
|
||||
sales_channel=bi.sales_channel)
|
||||
sales_channel=sc)
|
||||
return layout_map
|
||||
|
||||
|
||||
@@ -167,7 +173,7 @@ def _ticket_layouts_for_item(request, item):
|
||||
request._ticket_layouts_for_item = {}
|
||||
if item.pk not in request._ticket_layouts_for_item:
|
||||
request._ticket_layouts_for_item[item.pk] = {
|
||||
tli.sales_channel: tli.layout
|
||||
tli.sales_channel.identifier: tli.layout
|
||||
for tli in item.ticketlayout_assignments.select_related('layout')
|
||||
}
|
||||
if request._ticket_layouts_for_item[item.pk] and 'web' not in request._ticket_layouts_for_item[item.pk]:
|
||||
@@ -184,10 +190,10 @@ def control_order_position_info(sender: Event, position, request, order, **kwarg
|
||||
layouts = []
|
||||
seen = set()
|
||||
lm = _ticket_layouts_for_item(request, position.item)
|
||||
if order.sales_channel in lm:
|
||||
seen.add(lm[order.sales_channel])
|
||||
if order.sales_channel.identifier in lm:
|
||||
seen.add(lm[order.sales_channel.identifier])
|
||||
for k, l in lm.items():
|
||||
if k == order.sales_channel or l in seen:
|
||||
if k == order.sales_channel.identifier or l in seen:
|
||||
continue
|
||||
layouts.append({
|
||||
'label': str(l.name),
|
||||
|
||||
@@ -75,8 +75,8 @@ class PdfTicketOutput(BaseTicketOutput):
|
||||
def layout_map(self):
|
||||
if not hasattr(self.event, '_ticketoutputpdf_cache_layoutmap'):
|
||||
self.event._ticketoutputpdf_cache_layoutmap = {
|
||||
(bi.item_id, bi.sales_channel): bi.layout
|
||||
for bi in TicketLayoutItem.objects.select_related('layout').filter(item__event=self.event)
|
||||
(bi.item_id, bi.sales_channel.identifier): bi.layout
|
||||
for bi in TicketLayoutItem.objects.select_related('layout', 'sales_channel').filter(item__event=self.event)
|
||||
}
|
||||
return self.event._ticketoutputpdf_cache_layoutmap
|
||||
|
||||
@@ -118,7 +118,7 @@ class PdfTicketOutput(BaseTicketOutput):
|
||||
for op in self.get_tickets_to_print(order):
|
||||
layout = override_layout.send_chained(
|
||||
order.event, 'layout', orderposition=op, layout=self.layout_map.get(
|
||||
(op.item_id, self.override_channel or order.sales_channel),
|
||||
(op.item_id, self.override_channel or order.sales_channel.identifier),
|
||||
self.layout_map.get(
|
||||
(op.item_id, 'web'),
|
||||
self.default_layout
|
||||
@@ -139,7 +139,7 @@ class PdfTicketOutput(BaseTicketOutput):
|
||||
|
||||
layout = override_layout.send_chained(
|
||||
order.event, 'layout', orderposition=op, layout=self.layout_map.get(
|
||||
(op.item_id, self.override_channel or order.sales_channel),
|
||||
(op.item_id, self.override_channel or order.sales_channel.identifier),
|
||||
self.layout_map.get(
|
||||
(op.item_id, 'web'),
|
||||
self.default_layout
|
||||
|
||||
Reference in New Issue
Block a user