Badges: Allow to disable per-product

This commit is contained in:
Raphael Michel
2019-02-01 16:20:30 +01:00
parent c9415cba2b
commit f77b551aa6
7 changed files with 113 additions and 5 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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