mirror of
https://github.com/pretix/pretix.git
synced 2026-05-04 15:04:03 +00:00
Badges: Allow to disable per-product
This commit is contained in:
@@ -9,6 +9,7 @@ from django.contrib.staticfiles import finders
|
||||
from django.core.files import File
|
||||
from django.core.files.base import ContentFile
|
||||
from django.core.files.storage import default_storage
|
||||
from django.db.models import Exists, OuterRef
|
||||
from django.db.models.functions import Coalesce
|
||||
from django.utils.translation import ugettext as _
|
||||
from jsonfallback.functions import JSONExtract
|
||||
@@ -20,11 +21,14 @@ from pretix.base.exporter import BaseExporter
|
||||
from pretix.base.i18n import language
|
||||
from pretix.base.models import Order, OrderPosition
|
||||
from pretix.base.pdf import Renderer
|
||||
from pretix.base.services.orders import OrderError
|
||||
from pretix.base.settings import PERSON_NAME_SCHEMES
|
||||
from pretix.plugins.badges.models import BadgeItem, BadgeLayout
|
||||
|
||||
|
||||
def _renderer(event, layout):
|
||||
if layout is None:
|
||||
return None
|
||||
if isinstance(layout.background, File) and layout.background.name:
|
||||
bgf = default_storage.open(layout.background.name, "rb")
|
||||
else:
|
||||
@@ -45,10 +49,12 @@ def render_pdf(event, positions):
|
||||
default_renderer = None
|
||||
merger = PdfFileMerger()
|
||||
|
||||
any = False
|
||||
for op in positions:
|
||||
r = renderermap.get(op.item_id, default_renderer)
|
||||
if not r:
|
||||
continue
|
||||
any = True
|
||||
|
||||
with language(op.order.locale):
|
||||
buffer = BytesIO()
|
||||
@@ -62,6 +68,8 @@ def render_pdf(event, positions):
|
||||
merger.write(outbuffer)
|
||||
merger.close()
|
||||
outbuffer.seek(0)
|
||||
if not any:
|
||||
raise OrderError(_("None of the selected products is configured to print badges."))
|
||||
return outbuffer
|
||||
|
||||
|
||||
@@ -76,7 +84,9 @@ class BadgeExporter(BaseExporter):
|
||||
[
|
||||
('items',
|
||||
forms.ModelMultipleChoiceField(
|
||||
queryset=self.event.items.all(),
|
||||
queryset=self.event.items.annotate(
|
||||
no_badging=Exists(BadgeItem.objects.filter(item=OuterRef('pk'), layout__isnull=True))
|
||||
).exclude(no_badging=True),
|
||||
label=_('Limit to products'),
|
||||
widget=forms.CheckboxSelectMultiple(
|
||||
attrs={'class': 'scrolling-multiple-choice'}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
from django import forms
|
||||
from django.forms import Field
|
||||
from django.forms.models import ModelChoiceIterator
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from pretix.plugins.badges.models import BadgeItem, BadgeLayout
|
||||
@@ -10,10 +12,40 @@ class BadgeLayoutForm(forms.ModelForm):
|
||||
fields = ('name',)
|
||||
|
||||
|
||||
NoLayoutSingleton = BadgeLayout(pk='-')
|
||||
|
||||
|
||||
class BadgeLayoutIterator(ModelChoiceIterator):
|
||||
|
||||
def __iter__(self):
|
||||
yield ("-", _("(Do not print badges)"))
|
||||
yield from super().__iter__()
|
||||
|
||||
def __len__(self):
|
||||
return super().__len__() + 1
|
||||
|
||||
|
||||
class BadgeLayoutChoiceField(forms.ModelChoiceField):
|
||||
iterator = BadgeLayoutIterator
|
||||
|
||||
def to_python(self, value):
|
||||
if value == '-':
|
||||
return NoLayoutSingleton
|
||||
return super().to_python(value)
|
||||
|
||||
def validate(self, value):
|
||||
if value == '-':
|
||||
return '-'
|
||||
return Field.validate(self, value)
|
||||
|
||||
|
||||
class BadgeItemForm(forms.ModelForm):
|
||||
layout = BadgeLayoutChoiceField(queryset=BadgeLayout.objects.none())
|
||||
|
||||
class Meta:
|
||||
model = BadgeItem
|
||||
fields = ('layout',)
|
||||
exclude = ('layout',)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
event = kwargs.pop('event')
|
||||
@@ -22,6 +54,10 @@ class BadgeItemForm(forms.ModelForm):
|
||||
self.fields['layout'].empty_label = _('(Event default)')
|
||||
self.fields['layout'].queryset = event.badge_layouts.all()
|
||||
self.fields['layout'].required = False
|
||||
if self.instance.pk and not self.instance.layout_id:
|
||||
self.initial['layout'] = NoLayoutSingleton
|
||||
elif self.instance.layout:
|
||||
self.initial['layout'] = self.instance.layout
|
||||
|
||||
def save(self, commit=True):
|
||||
if self.cleaned_data['layout'] is None:
|
||||
@@ -29,5 +65,9 @@ class BadgeItemForm(forms.ModelForm):
|
||||
self.instance.delete()
|
||||
else:
|
||||
return
|
||||
elif self.cleaned_data['layout'] is NoLayoutSingleton:
|
||||
self.instance.layout = None
|
||||
self.instance.save()
|
||||
else:
|
||||
self.instance.layout = self.cleaned_data['layout']
|
||||
return super().save(commit=commit)
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
# Generated by Django 2.1.5 on 2019-02-01 14:24
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('badges', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='badgelayout',
|
||||
options={'ordering': ('name',)},
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='badgeitem',
|
||||
name='layout',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='item_assignments', to='badges.BadgeLayout'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='badgelayout',
|
||||
name='default',
|
||||
field=models.BooleanField(default=False, verbose_name='Default'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='badgelayout',
|
||||
name='layout',
|
||||
field=models.TextField(default='[{"type":"textarea","left":"13.09","bottom":"49.73","fontsize":"23.6","color":[0,0,0,1],"fontfamily":"Open Sans","bold":true,"italic":false,"width":"121.83","content":"attendee_name","text":"Max Mustermann","align":"center"}]'),
|
||||
),
|
||||
]
|
||||
@@ -46,6 +46,9 @@ class BadgeLayout(LoggedModel):
|
||||
|
||||
|
||||
class BadgeItem(models.Model):
|
||||
# If no BadgeItem exists => use default
|
||||
# If BadgeItem exists with layout=None => don't print
|
||||
item = models.OneToOneField('pretixbase.Item', null=True, blank=True, related_name='badge_assignment',
|
||||
on_delete=models.CASCADE)
|
||||
layout = models.ForeignKey('BadgeLayout', on_delete=models.CASCADE, related_name='item_assignments')
|
||||
layout = models.ForeignKey('BadgeLayout', on_delete=models.CASCADE, related_name='item_assignments',
|
||||
null=True, blank=True)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import copy
|
||||
import json
|
||||
from collections import defaultdict
|
||||
|
||||
from django.dispatch import receiver
|
||||
from django.template.loader import get_template
|
||||
@@ -97,11 +98,27 @@ def register_pdf(sender, **kwargs):
|
||||
return BadgeExporter
|
||||
|
||||
|
||||
def _cached_rendermap(event):
|
||||
if hasattr(event, '_cached_rendermap'):
|
||||
return event._cached_renderermap
|
||||
renderermap = {
|
||||
bi.item_id: bi.layout_id
|
||||
for bi in BadgeItem.objects.select_related('layout').filter(item__event=event)
|
||||
}
|
||||
try:
|
||||
default_renderer = event.badge_layouts.get(default=True).pk
|
||||
except BadgeLayout.DoesNotExist:
|
||||
default_renderer = None
|
||||
event._cached_renderermap = defaultdict(lambda: default_renderer)
|
||||
event._cached_renderermap.update(renderermap)
|
||||
return event._cached_renderermap
|
||||
|
||||
|
||||
@receiver(order_position_buttons, dispatch_uid="badges_control_order_buttons")
|
||||
def control_order_position_info(sender: Event, position, request, order: Order, **kwargs):
|
||||
|
||||
if _cached_rendermap(sender)[position.item_id] is None:
|
||||
return ''
|
||||
template = get_template('pretixplugins/badges/control_order_position_buttons.html')
|
||||
|
||||
ctx = {
|
||||
'order': order,
|
||||
'request': request,
|
||||
@@ -113,6 +130,9 @@ def control_order_position_info(sender: Event, position, request, order: Order,
|
||||
|
||||
@receiver(order_info, dispatch_uid="badges_control_order_info")
|
||||
def control_order_info(sender: Event, request, order: Order, **kwargs):
|
||||
cm = _cached_rendermap(sender)
|
||||
if all(cm[p.item_id] is None for p in order.positions.all()):
|
||||
return ''
|
||||
|
||||
template = get_template('pretixplugins/badges/control_order_info.html')
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ from django.core.files.base import ContentFile
|
||||
from pretix.base.models import (
|
||||
CachedFile, Event, OrderPosition, cachedfile_name,
|
||||
)
|
||||
from pretix.base.services.orders import OrderError
|
||||
from pretix.celery_app import app
|
||||
|
||||
from .exporters import render_pdf
|
||||
@@ -13,7 +14,7 @@ from .exporters import render_pdf
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@app.task()
|
||||
@app.task(throws=(OrderError,))
|
||||
def badges_create_pdf(fileid: int, event: int, positions: List[int]) -> int:
|
||||
file = CachedFile.objects.get(id=fileid)
|
||||
event = Event.objects.get(id=event)
|
||||
|
||||
@@ -192,6 +192,7 @@ class LayoutEditorView(BaseEditorView):
|
||||
class OrderPrintDo(EventPermissionRequiredMixin, AsyncAction, View):
|
||||
task = badges_create_pdf
|
||||
permission = 'can_view_orders'
|
||||
known_errortypes = ['OrderError']
|
||||
|
||||
def get_success_message(self, value):
|
||||
return None
|
||||
|
||||
Reference in New Issue
Block a user