New implementation of sales channels (#4111)

Co-authored-by: Martin Gross <gross@rami.io>
This commit is contained in:
Raphael Michel
2024-06-30 19:24:30 +02:00
committed by GitHub
parent 95511b0330
commit 4fb5c6bef0
174 changed files with 2902 additions and 616 deletions

View File

@@ -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

View File

@@ -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,
}

View File

@@ -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

View File

@@ -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()

View File

@@ -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")},
),
]

View File

@@ -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),
]

View File

@@ -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",
),
),
]

View File

@@ -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'),)

View File

@@ -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),

View File

@@ -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