def build_notification(self, logentry: LogEntry): order = logentry.content_object order_url = build_absolute_uri( 'control:event.order', kwargs={ 'organizer': logentry.event.organizer.slug, 'event': logentry.event.slug, 'code': order.code } ) n = Notification( event=logentry.event, title=self._title.format(order=order, event=logentry.event), url=order_url ) n.add_attribute(_('Event'), order.event.name) n.add_attribute(_('Order code'), order.code) n.add_attribute(_('Order total'), money_filter(order.total, logentry.event.currency)) n.add_attribute(_('Pending amount'), money_filter(order.pending_sum, logentry.event.currency)) n.add_attribute(_('Order date'), date_format(order.datetime, 'SHORT_DATETIME_FORMAT')) n.add_attribute(_('Order status'), order.get_status_display()) n.add_attribute(_('Order positions'), str(order.positions.count())) n.add_action(_('View order details'), order_url) return n
def _label(self, event, item_or_variation, avail, override_price=None): if isinstance(item_or_variation, ItemVariation): variation = item_or_variation item = item_or_variation.item price = variation.price label = variation.value else: item = item_or_variation price = item.default_price label = item.name if override_price: price = override_price if self.price_included: price = TAXED_ZERO else: price = item.tax(price) if not price.gross: n = '{name}'.format( name=label ) elif not price.rate: n = _('{name} (+ {price})').format( name=label, price=money_filter(price.gross, event.currency) ) elif event.settings.display_net_prices: n = _('{name} (+ {price} plus {taxes}% {taxname})').format( name=label, price=money_filter(price.net, event.currency), taxes=number_format(price.rate), taxname=price.name ) else: n = _('{name} (+ {price} incl. {taxes}% {taxname})').format( name=label, price=money_filter(price.gross, event.currency), taxes=number_format(price.rate), taxname=price.name ) if avail[0] < 20: n += ' – {}'.format(_('SOLD OUT')) elif avail[0] < 100: n += ' – {}'.format(_('Currently unavailable')) else: if avail[1] is not None and event.settings.show_quota_left: n += ' – {}'.format(_('%(num)s currently available') % {'num': avail[1]}) if not isinstance(item_or_variation, ItemVariation) and item.picture: n = escape(n) n += '<br>' n += '<a href="{}" class="productpicture" data-title="{}" data-lightbox={}>'.format( item.picture.url, escape(escape(item.name)), item.id ) n += '<img src="{}" alt="{}">'.format( thumb(item.picture, '60x60^'), escape(item.name) ) n += '</a>' n = mark_safe(n) return n
def get(self, request, *args, **kwargs): u = request.GET.get('query', '') if len(u) < 2: return JsonResponse({'results': []}) if "-" in u: code = (Q(event__slug__icontains=u.split("-")[0]) & Q(code__icontains=Order.normalize_code(u.split("-")[1]))) else: code = Q(code__icontains=Order.normalize_code(u)) qs = self.order_qs().order_by('pk').annotate(inr=Concat('invoices__prefix', 'invoices__invoice_no')).filter( code | Q(email__icontains=u) | Q(positions__attendee_name_cached__icontains=u) | Q(positions__attendee_email__icontains=u) | Q(invoice_address__name_cached__icontains=u) | Q(invoice_address__company__icontains=u) | Q(invoices__invoice_no=u) | Q(invoices__invoice_no=u.zfill(5)) | Q(inr=u) ).select_related('event').annotate(pcnt=Count('invoices')).distinct() # Yep, we wouldn't need to count the invoices here. However, having this Count() statement in there # tricks Django into generating a GROUP BY clause that it otherwise wouldn't and that is required to # avoid duplicate results. Yay? return JsonResponse({ 'results': [ { 'code': o.event.slug.upper() + '-' + o.code, 'status': o.get_status_display(), 'total': money_filter(o.total, o.event.currency) } for o in qs ] })
def format_map(self, order): return { 'order': order.code, 'total': order.total, 'currency': self.event.currency, 'total_with_currency': money_filter(order.total, self.event.currency) }
def __str__(self): return money_filter(self.value, self.currency)
"label": _("Product category"), "editor_sample": _("Ticket category"), "evaluate": lambda orderposition, order, event: (str(orderposition.item.category.name) if orderposition.item.category else "") }), ("price", { "label": _("Price"), "editor_sample": _("123.45 EUR"), "evaluate": lambda op, order, event: money_filter(op.price, event.currency) }), ("attendee_name", { "label": _("Attendee name"), "editor_sample": _("John Doe"), "evaluate": lambda op, order, ev: op.attendee_name or (op.addon_to.attendee_name if op.addon_to else '') }), ("event_name", { "label": _("Event name"), "editor_sample": _("Sample event name"), "evaluate": lambda op, order, ev: str(ev.name) }),
def _get_story(self, doc): has_taxes = any(il.tax_value for il in self.invoice.lines.all()) story = [ NextPageTemplate('FirstPage'), Paragraph(pgettext('invoice', 'Invoice') if not self.invoice.is_cancellation else pgettext('invoice', 'Cancellation'), self.stylesheet['Heading1']), Spacer(1, 5 * mm), NextPageTemplate('OtherPages'), ] if self.invoice.internal_reference: story.append(Paragraph( pgettext('invoice', 'Customer reference: {reference}').format(reference=self.invoice.internal_reference), self.stylesheet['Normal'] )) if self.invoice.introductory_text: story.append(Paragraph(self.invoice.introductory_text, self.stylesheet['Normal'])) story.append(Spacer(1, 10 * mm)) taxvalue_map = defaultdict(Decimal) grossvalue_map = defaultdict(Decimal) tstyledata = [ ('ALIGN', (1, 0), (-1, -1), 'RIGHT'), ('VALIGN', (0, 0), (-1, -1), 'TOP'), ('FONTNAME', (0, 0), (-1, 0), 'OpenSansBd'), ('FONTNAME', (0, -1), (-1, -1), 'OpenSansBd'), ('LEFTPADDING', (0, 0), (0, -1), 0), ('RIGHTPADDING', (-1, 0), (-1, -1), 0), ] if has_taxes: tdata = [( pgettext('invoice', 'Description'), pgettext('invoice', 'Qty'), pgettext('invoice', 'Tax rate'), pgettext('invoice', 'Net'), pgettext('invoice', 'Gross'), )] else: tdata = [( pgettext('invoice', 'Description'), pgettext('invoice', 'Qty'), pgettext('invoice', 'Amount'), )] total = Decimal('0.00') for line in self.invoice.lines.all(): if has_taxes: tdata.append(( Paragraph(line.description, self.stylesheet['Normal']), "1", localize(line.tax_rate) + " %", money_filter(line.net_value, self.invoice.event.currency), money_filter(line.gross_value, self.invoice.event.currency), )) else: tdata.append(( Paragraph(line.description, self.stylesheet['Normal']), "1", money_filter(line.gross_value, self.invoice.event.currency), )) taxvalue_map[line.tax_rate, line.tax_name] += line.tax_value grossvalue_map[line.tax_rate, line.tax_name] += line.gross_value total += line.gross_value if has_taxes: tdata.append([ pgettext('invoice', 'Invoice total'), '', '', '', money_filter(total, self.invoice.event.currency) ]) colwidths = [a * doc.width for a in (.50, .05, .15, .15, .15)] else: tdata.append([ pgettext('invoice', 'Invoice total'), '', money_filter(total, self.invoice.event.currency) ]) colwidths = [a * doc.width for a in (.65, .05, .30)] table = Table(tdata, colWidths=colwidths, repeatRows=1) table.setStyle(TableStyle(tstyledata)) story.append(table) story.append(Spacer(1, 15 * mm)) if self.invoice.payment_provider_text: story.append(Paragraph(self.invoice.payment_provider_text, self.stylesheet['Normal'])) if self.invoice.additional_text: story.append(Paragraph(self.invoice.additional_text, self.stylesheet['Normal'])) story.append(Spacer(1, 15 * mm)) tstyledata = [ ('ALIGN', (1, 0), (-1, -1), 'RIGHT'), ('LEFTPADDING', (0, 0), (0, -1), 0), ('RIGHTPADDING', (-1, 0), (-1, -1), 0), ('FONTSIZE', (0, 0), (-1, -1), 8), ('FONTNAME', (0, 0), (-1, -1), 'OpenSans'), ] thead = [ pgettext('invoice', 'Tax rate'), pgettext('invoice', 'Net value'), pgettext('invoice', 'Gross value'), pgettext('invoice', 'Tax'), '' ] tdata = [thead] for idx, gross in grossvalue_map.items(): rate, name = idx if rate == 0: continue tax = taxvalue_map[idx] tdata.append([ localize(rate) + " % " + name, money_filter(gross - tax, self.invoice.event.currency), money_filter(gross, self.invoice.event.currency), money_filter(tax, self.invoice.event.currency), '' ]) def fmt(val): try: return vat_moss.exchange_rates.format(val, self.invoice.foreign_currency_display) except ValueError: return localize(val) + ' ' + self.invoice.foreign_currency_display if len(tdata) > 1 and has_taxes: colwidths = [a * doc.width for a in (.25, .15, .15, .15, .3)] table = Table(tdata, colWidths=colwidths, repeatRows=2, hAlign=TA_LEFT) table.setStyle(TableStyle(tstyledata)) story.append(KeepTogether([ Paragraph(pgettext('invoice', 'Included taxes'), self.stylesheet['FineprintHeading']), table ])) if self.invoice.foreign_currency_display and self.invoice.foreign_currency_rate: tdata = [thead] for idx, gross in grossvalue_map.items(): rate, name = idx if rate == 0: continue tax = taxvalue_map[idx] gross = round_decimal(gross * self.invoice.foreign_currency_rate) tax = round_decimal(tax * self.invoice.foreign_currency_rate) net = gross - tax tdata.append([ localize(rate) + " % " + name, fmt(net), fmt(gross), fmt(tax), '' ]) table = Table(tdata, colWidths=colwidths, repeatRows=2, hAlign=TA_LEFT) table.setStyle(TableStyle(tstyledata)) story.append(KeepTogether([ Spacer(1, height=2 * mm), Paragraph( pgettext( 'invoice', 'Using the conversion rate of 1:{rate} as published by the European Central Bank on ' '{date}, this corresponds to:' ).format(rate=localize(self.invoice.foreign_currency_rate), date=date_format(self.invoice.foreign_currency_rate_date, "SHORT_DATE_FORMAT")), self.stylesheet['Fineprint'] ), Spacer(1, height=3 * mm), table ])) elif self.invoice.foreign_currency_display and self.invoice.foreign_currency_rate: story.append(Spacer(1, 5 * mm)) story.append(Paragraph( pgettext( 'invoice', 'Using the conversion rate of 1:{rate} as published by the European Central Bank on ' '{date}, the invoice total corresponds to {total}.' ).format(rate=localize(self.invoice.foreign_currency_rate), date=date_format(self.invoice.foreign_currency_rate_date, "SHORT_DATE_FORMAT"), total=fmt(total)), self.stylesheet['Fineprint'] )) return story
"evaluate": lambda orderposition, order, event: ( '{} - {}'.format(orderposition.item.name, orderposition.variation) if orderposition.variation else str(orderposition.item.name) ) }), ("item_category", { "label": _("Product category"), "editor_sample": _("Ticket category"), "evaluate": lambda orderposition, order, event: ( str(orderposition.item.category.name) if orderposition.item.category else "" ) }), ("price", { "label": _("Price"), "editor_sample": _("123.45 EUR"), "evaluate": lambda op, order, event: money_filter(op.price, event.currency) }), ("attendee_name", { "label": _("Attendee name"), "editor_sample": _("John Doe"), "evaluate": lambda op, order, ev: op.attendee_name or (op.addon_to.attendee_name if op.addon_to else '') }), ("event_name", { "label": _("Event name"), "editor_sample": _("Sample event name"), "evaluate": lambda op, order, ev: str(ev.name) }), ("event_date", { "label": _("Event date"), "editor_sample": _("May 31st, 2017"), "evaluate": lambda op, order, ev: ev.get_date_from_display(show_times=False)
def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.fields['price'].widget.attrs['placeholder'] = money_filter(self.item.default_price, self.item.event.currency, hide_currency=True) self.fields['price'].label = str(self.item)
def _display_order_changed(event: Event, logentry: LogEntry): data = json.loads(logentry.data) text = _('The order has been changed:') if logentry.action_type == 'pretix.event.order.changed.item': old_item = str(event.items.get(pk=data['old_item'])) if data['old_variation']: old_item += ' - ' + str( ItemVariation.objects.get(item__event=event, pk=data['old_variation'])) new_item = str(event.items.get(pk=data['new_item'])) if data['new_variation']: new_item += ' - ' + str( ItemVariation.objects.get(item__event=event, pk=data['new_variation'])) return text + ' ' + _( 'Position #{posid}: {old_item} ({old_price}) changed ' 'to {new_item} ({new_price}).').format( posid=data.get('positionid', '?'), old_item=old_item, new_item=new_item, old_price=money_filter(Decimal(data['old_price']), event.currency), new_price=money_filter(Decimal(data['new_price']), event.currency), ) elif logentry.action_type == 'pretix.event.order.changed.seat': return text + ' ' + _('Position #{posid}: Seat "{old_seat}" changed ' 'to "{new_seat}".').format( posid=data.get('positionid', '?'), old_seat=data.get('old_seat'), new_seat=data.get('new_seat'), ) elif logentry.action_type == 'pretix.event.order.changed.subevent': old_se = str(event.subevents.get(pk=data['old_subevent'])) new_se = str(event.subevents.get(pk=data['new_subevent'])) return text + ' ' + _( 'Position #{posid}: Event date "{old_event}" ({old_price}) changed ' 'to "{new_event}" ({new_price}).').format( posid=data.get('positionid', '?'), old_event=old_se, new_event=new_se, old_price=money_filter(Decimal(data['old_price']), event.currency), new_price=money_filter(Decimal(data['new_price']), event.currency), ) elif logentry.action_type == 'pretix.event.order.changed.price': return text + ' ' + _( 'Price of position #{posid} changed from {old_price} ' 'to {new_price}.').format( posid=data.get('positionid', '?'), old_price=money_filter(Decimal(data['old_price']), event.currency), new_price=money_filter(Decimal(data['new_price']), event.currency), ) elif logentry.action_type == 'pretix.event.order.changed.tax_rule': if 'positionid' in data: return text + ' ' + _( 'Tax rule of position #{posid} changed from {old_rule} ' 'to {new_rule}.').format( posid=data.get('positionid', '?'), old_rule=TaxRule.objects.get(pk=data['old_taxrule']) if data['old_taxrule'] else '–', new_rule=TaxRule.objects.get(pk=data['new_taxrule']), ) elif 'fee' in data: return text + ' ' + _( 'Tax rule of fee #{fee} changed from {old_rule} ' 'to {new_rule}.').format( fee=data.get('fee', '?'), old_rule=TaxRule.objects.get(pk=data['old_taxrule']) if data['old_taxrule'] else '–', new_rule=TaxRule.objects.get(pk=data['new_taxrule']), ) elif logentry.action_type == 'pretix.event.order.changed.addfee': return text + ' ' + str(_('A fee has been added')) elif logentry.action_type == 'pretix.event.order.changed.feevalue': return text + ' ' + _( 'A fee was changed from {old_price} to {new_price}.').format( old_price=money_filter(Decimal(data['old_price']), event.currency), new_price=money_filter(Decimal(data['new_price']), event.currency), ) elif logentry.action_type == 'pretix.event.order.changed.cancelfee': return text + ' ' + _('A fee of {old_price} was removed.').format( old_price=money_filter(Decimal(data['old_price']), event.currency), ) elif logentry.action_type == 'pretix.event.order.changed.cancel': old_item = str(event.items.get(pk=data['old_item'])) if data['old_variation']: old_item += ' - ' + str( ItemVariation.objects.get(pk=data['old_variation'])) return text + ' ' + _( 'Position #{posid} ({old_item}, {old_price}) canceled.').format( posid=data.get('positionid', '?'), old_item=old_item, old_price=money_filter(Decimal(data['old_price']), event.currency), ) elif logentry.action_type == 'pretix.event.order.changed.add': item = str(event.items.get(pk=data['item'])) if data['variation']: item += ' - ' + str( ItemVariation.objects.get(item__event=event, pk=data['variation'])) if data['addon_to']: addon_to = OrderPosition.objects.get(order__event=event, pk=data['addon_to']) return text + ' ' + _( 'Position #{posid} created: {item} ({price}) as an add-on to ' 'position #{addon_to}.').format( posid=data.get('positionid', '?'), item=item, addon_to=addon_to.positionid, price=money_filter(Decimal(data['price']), event.currency), ) else: return text + ' ' + _( 'Position #{posid} created: {item} ({price}).').format( posid=data.get('positionid', '?'), item=item, price=money_filter(Decimal(data['price']), event.currency), ) elif logentry.action_type == 'pretix.event.order.changed.secret': return text + ' ' + _( 'A new secret has been generated for position #{posid}.').format( posid=data.get('positionid', '?'), ) elif logentry.action_type == 'pretix.event.order.changed.split': old_item = str(event.items.get(pk=data['old_item'])) if data['old_variation']: old_item += ' - ' + str( ItemVariation.objects.get(pk=data['old_variation'])) url = reverse('control:event.order', kwargs={ 'event': event.slug, 'organizer': event.organizer.slug, 'code': data['new_order'] }) return mark_safe( escape(text) + ' ' + _('Position #{posid} ({old_item}, {old_price}) split into new order: {order}' ).format( old_item=escape(old_item), posid=data.get('positionid', '?'), order='<a href="{}">{}</a>'.format(url, data['new_order']), old_price=money_filter(Decimal(data['old_price']), event.currency), )) elif logentry.action_type == 'pretix.event.order.changed.split_from': return _('This order has been created by splitting the order {order}' ).format(order=data['original_order'], )
"evaluate": lambda orderposition, order, event: ( '{} - {}'.format(orderposition.item.name, orderposition.variation) if orderposition.variation else str(orderposition.item.name) ) }), ("item_category", { "label": _("Product category"), "editor_sample": _("Ticket category"), "evaluate": lambda orderposition, order, event: ( str(orderposition.item.category.name) if orderposition.item.category else "" ) }), ("price", { "label": _("Price"), "editor_sample": _("123.45 EUR"), "evaluate": lambda op, order, event: money_filter(op.price, event.currency) }), ("price_with_addons", { "label": _("Price including add-ons"), "editor_sample": _("123.45 EUR"), "evaluate": lambda op, order, event: money_filter(op.price + sum( p.price for p in op.addons.all() if not p.canceled ), event.currency) }), ("attendee_name", { "label": _("Attendee name"), "editor_sample": _("John Doe"), "evaluate": lambda op, order, ev: op.attendee_name or (op.addon_to.attendee_name if op.addon_to else '') }),
def build_notification(self, logentry: LogEntry): order = logentry.content_object order_url = build_absolute_uri('control:event.order', kwargs={ 'organizer': logentry.event.organizer.slug, 'event': logentry.event.slug, 'code': order.code }) n = Notification(event=logentry.event, title=self._title.format(order=order, event=logentry.event), url=order_url) n.add_attribute(_('Event'), order.event.name) if order.event.has_subevents: ses = [] for se in self.event.subevents.filter( id__in=order.positions.values_list('subevent', flat=True)): ses.append('{} ({})'.format(se.name, se.get_date_range_display())) n.add_attribute(pgettext_lazy('subevent', 'Dates'), '\n'.join(ses)) else: n.add_attribute(_('Event date'), order.event.get_date_range_display()) positions = list( order.positions.select_related('item', 'variation', 'subevent')) fees = list(order.fees.all()) n.add_attribute(_('Order code'), order.code) n.add_attribute( _('Net total'), money_filter( sum([p.net_price for p in positions] + [f.net_value for f in fees]), logentry.event.currency)) n.add_attribute(_('Order total'), money_filter(order.total, logentry.event.currency)) n.add_attribute( _('Pending amount'), money_filter(order.pending_sum, logentry.event.currency)) n.add_attribute( _('Order date'), date_format(order.datetime.astimezone(logentry.event.timezone), 'SHORT_DATETIME_FORMAT')) n.add_attribute(_('Order status'), order.get_status_display()) n.add_attribute(_('Order positions'), str(order.positions.count())) def sortkey(op): return op.item_id, op.variation_id, op.subevent_id def groupkey(op): return op.item, op.variation, op.subevent cart = [ (k, list(v)) for k, v in groupby(sorted(positions, key=sortkey), key=groupkey) ] items = [] for (item, variation, subevent), pos in cart: ele = [str(len(pos)) + 'x ' + str(item)] if variation: ele.append(str(variation.value)) if subevent: ele.append(str(subevent)) items.append(' – '.join(ele)) n.add_attribute(_('Purchased products'), '\n'.join(items)) n.add_action(_('View order details'), order_url) return n
def get_story(self, doc, form_data): cl = self.event.checkin_lists.get(pk=form_data['list']) questions = list( Question.objects.filter(event=self.event, id__in=form_data['questions'])) headlinestyle = self.get_style() headlinestyle.fontSize = 15 headlinestyle.fontName = 'OpenSansBd' colwidths = [3 * mm, 8 * mm, 8 * mm] + [ a * (doc.width - 8 * mm) for a in [.1, .25, (.25 if questions else .60)] + ([.35 / len(questions)] * len(questions) if questions else []) ] tstyledata = [ ('VALIGN', (0, 0), (-1, 0), 'BOTTOM'), ('ALIGN', (2, 0), (2, 0), 'CENTER'), ('VALIGN', (0, 1), (-1, -1), 'TOP'), ('FONTNAME', (0, 0), (-1, 0), 'OpenSansBd'), ('ALIGN', (0, 0), (0, -1), 'CENTER'), ('TEXTCOLOR', (0, 0), (0, -1), '#990000'), ('FONTNAME', (0, 0), (0, -1), 'OpenSansBd'), ] story = [ Paragraph(cl.name, headlinestyle), ] if cl.subevent: story += [ Spacer(1, 3 * mm), Paragraph( '{} ({} {})'.format( cl.subevent.name, cl.subevent.get_date_range_display(), date_format(cl.subevent.date_from, 'SHORT_TIME_FORMAT')), self.get_style()), ] story += [Spacer(1, 5 * mm)] tdata = [ [ '', '', # Translators: maximum 5 characters TableTextRotate(pgettext('tablehead', 'paid')), _('Order'), _('Name'), _('Product') + ' / ' + _('Price'), ], ] headrowstyle = self.get_style() headrowstyle.fontName = 'OpenSansBd' for q in questions: txt = str(q.question) p = Paragraph(txt, headrowstyle) while p.wrap(colwidths[len(tdata[0])], 5000)[1] > 30 * mm: txt = txt[:len(txt) - 50] + "..." p = Paragraph(txt, headrowstyle) tdata[0].append(p) qs = self._get_queryset(cl, form_data) for op in qs: try: ian = op.order.invoice_address.name iac = op.order.invoice_address.company except: ian = "" iac = "" name = op.attendee_name or (op.addon_to.attendee_name if op.addon_to else '') or ian if iac: name += "<br/>" + iac item = "{} ({})".format( str(op.item) + (" – " + str(op.variation.value) if op.variation else ""), money_filter(op.price, self.event.currency), ) if self.event.has_subevents and not cl.subevent: item += '<br/>{} ({})'.format( op.subevent.name, date_format(op.subevent.date_from, 'SHORT_DATETIME_FORMAT')) if op.seat: item += '<br/>' + str(op.seat) row = [ '!!' if op.item.checkin_attention or op.order.checkin_attention else '', CBFlowable(bool(op.last_checked_in)), '✘' if op.order.status != Order.STATUS_PAID else '✔', op.order.code, Paragraph(name, self.get_style()), Paragraph(item, self.get_style()), ] acache = {} if op.addon_to: for a in op.addon_to.answers.all(): # We do not want to localize Date, Time and Datetime question answers, as those can lead # to difficulties parsing the data (for example 2019-02-01 may become Février, 2019 01 in French). if a.question.type in Question.UNLOCALIZED_TYPES: acache[a.question_id] = a.answer else: acache[a.question_id] = str(a) for a in op.answers.all(): # We do not want to localize Date, Time and Datetime question answers, as those can lead # to difficulties parsing the data (for example 2019-02-01 may become Février, 2019 01 in French). if a.question.type in Question.UNLOCALIZED_TYPES: acache[a.question_id] = a.answer else: acache[a.question_id] = str(a) for q in questions: txt = acache.get(q.pk, '') p = Paragraph(txt, self.get_style()) while p.wrap(colwidths[len(row)], 5000)[1] > 50 * mm: txt = txt[:len(txt) - 50] + "..." p = Paragraph(txt, self.get_style()) row.append(p) if op.order.status != Order.STATUS_PAID: tstyledata += [ ('BACKGROUND', (2, len(tdata)), (2, len(tdata)), '#990000'), ('TEXTCOLOR', (2, len(tdata)), (2, len(tdata)), '#ffffff'), ('ALIGN', (2, len(tdata)), (2, len(tdata)), 'CENTER'), ] tdata.append(row) table = Table(tdata, colWidths=colwidths, repeatRows=1) table.setStyle(TableStyle(tstyledata)) story.append(table) return story
def _display_order_changed(event: Event, logentry: LogEntry): data = json.loads(logentry.data) text = _('The order has been changed:') if logentry.action_type == 'pretix.event.order.changed.item': old_item = str(event.items.get(pk=data['old_item'])) if data['old_variation']: old_item += ' - ' + str( ItemVariation.objects.get(item__event=event, pk=data['old_variation'])) new_item = str(event.items.get(pk=data['new_item'])) if data['new_variation']: new_item += ' - ' + str( ItemVariation.objects.get(item__event=event, pk=data['new_variation'])) return text + ' ' + _( 'Position #{posid}: {old_item} ({old_price}) changed ' 'to {new_item} ({new_price}).').format( posid=data.get('positionid', '?'), old_item=old_item, new_item=new_item, old_price=money_filter(Decimal(data['old_price']), event.currency), new_price=money_filter(Decimal(data['new_price']), event.currency), ) elif logentry.action_type == 'pretix.event.order.changed.subevent': old_se = str(event.subevents.get(pk=data['old_subevent'])) new_se = str(event.subevents.get(pk=data['new_subevent'])) return text + ' ' + _( 'Position #{posid}: Event date "{old_event}" ({old_price}) changed ' 'to "{new_event}" ({new_price}).').format( posid=data.get('positionid', '?'), old_event=old_se, new_event=new_se, old_price=money_filter(Decimal(data['old_price']), event.currency), new_price=money_filter(Decimal(data['new_price']), event.currency), ) elif logentry.action_type == 'pretix.event.order.changed.price': return text + ' ' + _( 'Price of position #{posid} changed from {old_price} ' 'to {new_price}.').format( posid=data.get('positionid', '?'), old_price=money_filter(Decimal(data['old_price']), event.currency), new_price=money_filter(Decimal(data['new_price']), event.currency), ) elif logentry.action_type == 'pretix.event.order.changed.cancel': old_item = str(event.items.get(pk=data['old_item'])) if data['old_variation']: old_item += ' - ' + str( ItemVariation.objects.get(pk=data['old_variation'])) return text + ' ' + _( 'Position #{posid} ({old_item}, {old_price}) removed.').format( posid=data.get('positionid', '?'), old_item=old_item, old_price=money_filter(Decimal(data['old_price']), event.currency), ) elif logentry.action_type == 'pretix.event.order.changed.add': item = str(event.items.get(pk=data['item'])) if data['variation']: item += ' - ' + str( ItemVariation.objects.get(item__event=event, pk=data['variation'])) if data['addon_to']: addon_to = OrderPosition.objects.get(order__event=event, pk=data['addon_to']) return text + ' ' + _( 'Position #{posid} created: {item} ({price}) as an add-on to ' 'position #{addon_to}.').format( posid=data.get('positionid', '?'), item=item, addon_to=addon_to.positionid, price=money_filter(Decimal(data['price']), event.currency), ) else: return text + ' ' + _( 'Position #{posid} created: {item} ({price}).').format( posid=data.get('positionid', '?'), item=item, price=money_filter(Decimal(data['price']), event.currency), ) elif logentry.action_type == 'pretix.event.order.changed.secret': return text + ' ' + _( 'A new secret has been generated for position #{posid}.').format( posid=data.get('positionid', '?'), ) elif logentry.action_type == 'pretix.event.order.changed.split': old_item = str(event.items.get(pk=data['old_item'])) if data['old_variation']: old_item += ' - ' + str( ItemVariation.objects.get(pk=data['old_variation'])) return text + ' ' + _( 'Position #{posid} ({old_item}, {old_price}) split into new order: {order}' ).format( old_item=old_item, posid=data.get('positionid', '?'), order=data['new_order'], old_price=money_filter(Decimal(data['old_price']), event.currency), ) elif logentry.action_type == 'pretix.event.order.changed.split_from': return _('This order has been created by splitting the order {order}' ).format(order=data['original_order'], )
def get_story(self, doc, form_data): cl = self.event.checkin_lists.get(pk=form_data['list']) questions = list(Question.objects.filter(event=self.event, id__in=form_data['questions'])) headlinestyle = self.get_style() headlinestyle.fontSize = 15 headlinestyle.fontName = 'OpenSansBd' colwidths = [3 * mm, 8 * mm, 8 * mm] + [ a * (doc.width - 8 * mm) for a in [.1, .25, (.25 if questions else .60)] + ( [.35 / len(questions)] * len(questions) if questions else [] ) ] tstyledata = [ ('VALIGN', (0, 0), (-1, 0), 'BOTTOM'), ('ALIGN', (2, 0), (2, 0), 'CENTER'), ('VALIGN', (0, 1), (-1, -1), 'TOP'), ('FONTNAME', (0, 0), (-1, 0), 'OpenSansBd'), ('ALIGN', (0, 0), (0, -1), 'CENTER'), ('TEXTCOLOR', (0, 0), (0, -1), '#990000'), ('FONTNAME', (0, 0), (0, -1), 'OpenSansBd'), ] story = [ Paragraph( '{} – {}'.format(cl.name, (cl.subevent or self.event).get_date_from_display()), headlinestyle ), Spacer(1, 5 * mm) ] tdata = [ [ '', '', # Translators: maximum 5 characters TableTextRotate(pgettext('tablehead', 'paid')), _('Order'), _('Name'), _('Product') + '\n' + _('Price'), ], ] headrowstyle = self.get_style() headrowstyle.fontName = 'OpenSansBd' for q in questions: txt = str(q.question) p = Paragraph(txt, headrowstyle) while p.wrap(colwidths[len(tdata[0])], 5000)[1] > 30 * mm: txt = txt[:len(txt) - 50] + "..." p = Paragraph(txt, headrowstyle) tdata[0].append(p) qs = self._get_queryset(cl, form_data) for op in qs: try: ian = op.order.invoice_address.name iac = op.order.invoice_address.company except: ian = "" iac = "" name = op.attendee_name or (op.addon_to.attendee_name if op.addon_to else '') or ian if iac: name += "<br/>" + iac row = [ '!!' if op.item.checkin_attention else '', CBFlowable(bool(op.last_checked_in)), '✘' if op.order.status != Order.STATUS_PAID else '✔', op.order.code, Paragraph(name, self.get_style()), Paragraph(str(op.item) + (" – " + str(op.variation.value) if op.variation else "") + "<br/>" + money_filter(op.price, self.event.currency), self.get_style()), ] acache = {} if op.addon_to: for a in op.addon_to.answers.all(): acache[a.question_id] = str(a) for a in op.answers.all(): acache[a.question_id] = str(a) for q in questions: txt = acache.get(q.pk, '') p = Paragraph(txt, self.get_style()) while p.wrap(colwidths[len(row)], 5000)[1] > 50 * mm: txt = txt[:len(txt) - 50] + "..." p = Paragraph(txt, self.get_style()) row.append(p) if op.order.status != Order.STATUS_PAID: tstyledata += [ ('BACKGROUND', (2, len(tdata)), (2, len(tdata)), '#990000'), ('TEXTCOLOR', (2, len(tdata)), (2, len(tdata)), '#ffffff'), ('ALIGN', (2, len(tdata)), (2, len(tdata)), 'CENTER'), ] tdata.append(row) table = Table(tdata, colWidths=colwidths, repeatRows=1) table.setStyle(TableStyle(tstyledata)) story.append(table) return story
def get_story(self, doc, form_data): cl = self.event.checkin_lists.get(pk=form_data['list']) questions = list( Question.objects.filter(event=self.event, id__in=form_data['questions'])) headlinestyle = self.get_style() headlinestyle.fontSize = 15 headlinestyle.fontName = 'OpenSansBd' colwidths = [3 * mm, 8 * mm, 8 * mm] + [ a * (doc.width - 8 * mm) for a in [.1, .25, (.25 if questions else .60)] + ([.35 / len(questions)] * len(questions) if questions else []) ] tstyledata = [ ('VALIGN', (0, 0), (-1, 0), 'BOTTOM'), ('ALIGN', (2, 0), (2, 0), 'CENTER'), ('VALIGN', (0, 1), (-1, -1), 'TOP'), ('FONTNAME', (0, 0), (-1, 0), 'OpenSansBd'), ('ALIGN', (0, 0), (0, -1), 'CENTER'), ('TEXTCOLOR', (0, 0), (0, -1), '#990000'), ('FONTNAME', (0, 0), (0, -1), 'OpenSansBd'), ] story = [ Paragraph( '{} – {}'.format(cl.name, (cl.subevent or self.event).get_date_from_display()), headlinestyle), Spacer(1, 5 * mm) ] tdata = [ [ '', '', # Translators: maximum 5 characters TableTextRotate(pgettext('tablehead', 'paid')), _('Order'), _('Name'), _('Product') + '\n' + _('Price'), ], ] headrowstyle = self.get_style() headrowstyle.fontName = 'OpenSansBd' for q in questions: txt = str(q.question) p = Paragraph(txt, headrowstyle) while p.wrap(colwidths[len(tdata[0])], 5000)[1] > 30 * mm: txt = txt[:len(txt) - 50] + "..." p = Paragraph(txt, headrowstyle) tdata[0].append(p) cqs = Checkin.objects.filter( position_id=OuterRef('pk'), list_id=cl.pk).order_by().values('position_id').annotate( m=Max('datetime')).values('m') qs = OrderPosition.objects.filter(order__event=self.event, ).annotate( last_checked_in=Subquery(cqs)).select_related( 'item', 'variation', 'order', 'addon_to', 'order__invoice_address').prefetch_related( 'answers', 'answers__question', 'addon_to__answers', 'addon_to__answers__question') if not cl.all_products: qs = qs.filter( item__in=cl.limit_products.values_list('id', flat=True)) if cl.subevent: qs = qs.filter(subevent=cl.subevent) if form_data['sort'] == 'name': qs = qs.order_by( Coalesce('attendee_name', 'addon_to__attendee_name', 'order__invoice_address__name'), 'order__code') elif form_data['sort'] == 'code': qs = qs.order_by('order__code') if not cl.include_pending: qs = qs.filter(order__status=Order.STATUS_PAID) else: qs = qs.filter(order__status__in=(Order.STATUS_PAID, Order.STATUS_PENDING)) for op in qs: try: ian = op.order.invoice_address.name iac = op.order.invoice_address.company except: ian = "" iac = "" name = op.attendee_name or (op.addon_to.attendee_name if op.addon_to else '') or ian if iac: name += "\n" + iac row = [ '!!' if op.item.checkin_attention else '', CBFlowable(bool(op.last_checked_in)), '✘' if op.order.status != Order.STATUS_PAID else '✔', op.order.code, Paragraph(name, self.get_style()), Paragraph( str(op.item) + (" – " + str(op.variation.value) if op.variation else "") + "<br/>" + money_filter(op.price, self.event.currency), self.get_style()), ] acache = {} if op.addon_to: for a in op.addon_to.answers.all(): acache[a.question_id] = str(a) for a in op.answers.all(): acache[a.question_id] = str(a) for q in questions: txt = acache.get(q.pk, '') p = Paragraph(txt, self.get_style()) while p.wrap(colwidths[len(row)], 5000)[1] > 50 * mm: txt = txt[:len(txt) - 50] + "..." p = Paragraph(txt, self.get_style()) row.append(p) if op.order.status != Order.STATUS_PAID: tstyledata += [ ('BACKGROUND', (2, len(tdata)), (2, len(tdata)), '#990000'), ('TEXTCOLOR', (2, len(tdata)), (2, len(tdata)), '#ffffff'), ('ALIGN', (2, len(tdata)), (2, len(tdata)), 'CENTER'), ] tdata.append(row) table = Table(tdata, colWidths=colwidths, repeatRows=1) table.setStyle(TableStyle(tstyledata)) story.append(table) return story
def print(self, currency): return '{} + {}% = {}'.format( money_filter(self.net, currency), localize(self.rate), money_filter(self.gross, currency) )
def __init__(self, *args, **kwargs): instance = kwargs.pop('instance') invoice_address = kwargs.pop('invoice_address') initial = kwargs.get('initial', {}) event = kwargs.pop('event') quota_cache = kwargs.pop('quota_cache') kwargs['initial'] = initial if instance.variation_id: initial['itemvar'] = f'{instance.item_id}-{instance.variation_id}' else: initial['itemvar'] = f'{instance.item_id}' super().__init__(*args, **kwargs) choices = [] i = instance.item pname = str(i) variations = list(i.variations.all()) if variations: current_quotas = (instance.variation.quotas.filter( subevent=instance.subevent) if instance.variation else instance.item.quotas.all( subevent=instance.subevent)) qa = QuotaAvailability() for v in variations: v._quotas = v.quotas.filter(subevent=instance.subevent) quotas_to_compute = [ q for q in v._quotas if q not in quota_cache ] qa.queue(*quotas_to_compute) qa.compute() quota_cache.update(qa.results) for v in variations: label = f'{i.name} – {v.value}' if instance.variation_id == v.id: choices.append((f'{i.pk}-{v.pk}', label)) continue if not v.active: continue q_res = [ (qa.results[q] if q in qa.results else quota_cache[q])[0] != Quota.AVAILABILITY_OK for q in v._quotas if q not in current_quotas ] if not v._quotas or (q_res and any(q_res)): continue new_price = get_price(i, v, voucher=instance.voucher, subevent=instance.subevent, invoice_address=invoice_address) current_price = TaxedPrice( tax=instance.tax_value, gross=instance.price, net=instance.price - instance.tax_value, name=instance.tax_rule.name if instance.tax_rule else '', rate=instance.tax_rate) if new_price.gross < current_price.gross and event.settings.change_allow_user_price == 'gte': continue if new_price.gross <= current_price.gross and event.settings.change_allow_user_price == 'gt': continue if new_price.gross != current_price.gross and event.settings.change_allow_user_price == 'eq': continue if new_price.gross < current_price.gross: if event.settings.display_net_prices: label += ' (- {} {})'.format( money_filter(current_price.gross - new_price.gross, event.currency), _('plus taxes')) else: label += ' (- {})'.format( money_filter(current_price.gross - new_price.gross, event.currency)) elif current_price.gross < new_price.gross: if event.settings.display_net_prices: label += ' ({}{} {})'.format( '+ ' if current_price.gross != Decimal('0.00') else '', money_filter(new_price.gross - current_price.gross, event.currency), _('plus taxes')) else: label += ' ({}{})'.format( '+ ' if current_price.gross != Decimal('0.00') else '', money_filter(new_price.gross - current_price.gross, event.currency)) choices.append((f'{i.pk}-{v.pk}', label)) if not choices: self.fields['itemvar'].widget.attrs['disabled'] = True self.fields['itemvar'].help_text = _( 'No other variation of this product is currently available for you.' ) else: choices.append((str(i.pk), '%s' % pname)) self.fields['itemvar'].widget.attrs['disabled'] = True self.fields['itemvar'].help_text = _( 'No other variations of this product exist.') self.fields['itemvar'].choices = choices
def _display_order_changed(event: Event, logentry: LogEntry): data = json.loads(logentry.data) text = _('The order has been changed:') if logentry.action_type == 'pretix.event.order.changed.item': old_item = str(event.items.get(pk=data['old_item'])) if data['old_variation']: old_item += ' - ' + str(ItemVariation.objects.get(item__event=event, pk=data['old_variation'])) new_item = str(event.items.get(pk=data['new_item'])) if data['new_variation']: new_item += ' - ' + str(ItemVariation.objects.get(item__event=event, pk=data['new_variation'])) return text + ' ' + _('Position #{posid}: {old_item} ({old_price}) changed ' 'to {new_item} ({new_price}).').format( posid=data.get('positionid', '?'), old_item=old_item, new_item=new_item, old_price=money_filter(Decimal(data['old_price']), event.currency), new_price=money_filter(Decimal(data['new_price']), event.currency), ) elif logentry.action_type == 'pretix.event.order.changed.subevent': old_se = str(event.subevents.get(pk=data['old_subevent'])) new_se = str(event.subevents.get(pk=data['new_subevent'])) return text + ' ' + _('Position #{posid}: Event date "{old_event}" ({old_price}) changed ' 'to "{new_event}" ({new_price}).').format( posid=data.get('positionid', '?'), old_event=old_se, new_event=new_se, old_price=money_filter(Decimal(data['old_price']), event.currency), new_price=money_filter(Decimal(data['new_price']), event.currency), ) elif logentry.action_type == 'pretix.event.order.changed.price': return text + ' ' + _('Price of position #{posid} changed from {old_price} ' 'to {new_price}.').format( posid=data.get('positionid', '?'), old_price=money_filter(Decimal(data['old_price']), event.currency), new_price=money_filter(Decimal(data['new_price']), event.currency), ) elif logentry.action_type == 'pretix.event.order.changed.cancel': old_item = str(event.items.get(pk=data['old_item'])) if data['old_variation']: old_item += ' - ' + str(ItemVariation.objects.get(pk=data['old_variation'])) return text + ' ' + _('Position #{posid} ({old_item}, {old_price}) removed.').format( posid=data.get('positionid', '?'), old_item=old_item, old_price=money_filter(Decimal(data['old_price']), event.currency), ) elif logentry.action_type == 'pretix.event.order.changed.add': item = str(event.items.get(pk=data['item'])) if data['variation']: item += ' - ' + str(ItemVariation.objects.get(item__event=event, pk=data['variation'])) if data['addon_to']: addon_to = OrderPosition.objects.get(order__event=event, pk=data['addon_to']) return text + ' ' + _('Position #{posid} created: {item} ({price}) as an add-on to ' 'position #{addon_to}.').format( posid=data.get('positionid', '?'), item=item, addon_to=addon_to.positionid, price=money_filter(Decimal(data['price']), event.currency), ) else: return text + ' ' + _('Position #{posid} created: {item} ({price}).').format( posid=data.get('positionid', '?'), item=item, price=money_filter(Decimal(data['price']), event.currency), ) elif logentry.action_type == 'pretix.event.order.changed.secret': return text + ' ' + _('A new secret has been generated for position #{posid}.').format( posid=data.get('positionid', '?'), ) elif logentry.action_type == 'pretix.event.order.changed.split': old_item = str(event.items.get(pk=data['old_item'])) if data['old_variation']: old_item += ' - ' + str(ItemVariation.objects.get(pk=data['old_variation'])) return text + ' ' + _('Position #{posid} ({old_item}, {old_price}) split into new order: {order}').format( old_item=old_item, posid=data.get('positionid', '?'), order=data['new_order'], old_price=money_filter(Decimal(data['old_price']), event.currency), ) elif logentry.action_type == 'pretix.event.order.changed.split_from': return _('This order has been created by splitting the order {order}').format( order=data['original_order'], )
def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.fields['price'].widget.attrs['placeholder'] = money_filter(self.variation.price, self.item.event.currency, hide_currency=True) self.fields['price'].label = '{} – {}'.format(str(self.item), self.variation.value)
def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.fields['price'].widget.attrs['placeholder'] = money_filter( self.variation.price, self.item.event.currency, hide_currency=True) self.fields['price'].label = '{} – {}'.format(str(self.item), self.variation.value)
def price_calc(self, request, *args, **kwargs): """ This calculates the price assuming a change of product or subevent. This endpoint is deliberately not documented and considered a private API, only to be used by pretix' web interface. Sample input: { "item": 2, "variation": null, "subevent": 3 } Sample output: { "gross": "2.34", "gross_formatted": "2,34", "net": "2.34", "tax": "0.00", "rate": "0.00", "name": "VAT" } """ serializer = PriceCalcSerializer(data=request.data, event=request.event) serializer.is_valid(raise_exception=True) data = serializer.validated_data pos = self.get_object() try: ia = pos.order.invoice_address except InvoiceAddress.DoesNotExist: ia = InvoiceAddress() kwargs = { 'item': pos.item, 'variation': pos.variation, 'voucher': pos.voucher, 'subevent': pos.subevent, 'addon_to': pos.addon_to, 'invoice_address': ia, } if data.get('item'): item = data.get('item') kwargs['item'] = item if item.has_variations: variation = data.get('variation') or pos.variation if not variation: raise ValidationError('No variation given') if variation.item != item: raise ValidationError('Variation does not belong to item') kwargs['variation'] = variation else: variation = None kwargs['variation'] = None if pos.voucher and not pos.voucher.applies_to(item, variation): kwargs['voucher'] = None if data.get('subevent'): kwargs['subevent'] = data.get('subevent') price = get_price(**kwargs) with language(data.get('locale') or self.request.event.settings.locale): return Response({ 'gross': price.gross, 'gross_formatted': money_filter(price.gross, self.request.event.currency, hide_currency=True), 'net': price.net, 'rate': price.rate, 'name': str(price.name), 'tax': price.tax, })
def print(self, currency): return '{} + {}% = {}'.format(money_filter(self.net, currency), localize(self.rate), money_filter(self.gross, currency))
def _get_story(self, doc): has_taxes = any(il.tax_value for il in self.invoice.lines.all()) story = [ NextPageTemplate('FirstPage'), Paragraph( ( pgettext('invoice', 'Tax Invoice') if str(self.invoice.invoice_from_country) == 'AU' else pgettext('invoice', 'Invoice') ) if not self.invoice.is_cancellation else pgettext('invoice', 'Cancellation'), self.stylesheet['Heading1'] ), Spacer(1, 5 * mm), NextPageTemplate('OtherPages'), ] story += self._get_intro() taxvalue_map = defaultdict(Decimal) grossvalue_map = defaultdict(Decimal) tstyledata = [ ('ALIGN', (1, 0), (-1, -1), 'RIGHT'), ('VALIGN', (0, 0), (-1, -1), 'TOP'), ('FONTNAME', (0, 0), (-1, 0), self.font_bold), ('FONTNAME', (0, -1), (-1, -1), self.font_bold), ('LEFTPADDING', (0, 0), (0, -1), 0), ('RIGHTPADDING', (-1, 0), (-1, -1), 0), ] if has_taxes: tdata = [( pgettext('invoice', 'Description'), pgettext('invoice', 'Qty'), pgettext('invoice', 'Tax rate'), pgettext('invoice', 'Net'), pgettext('invoice', 'Gross'), )] else: tdata = [( pgettext('invoice', 'Description'), pgettext('invoice', 'Qty'), pgettext('invoice', 'Amount'), )] total = Decimal('0.00') for line in self.invoice.lines.all(): if has_taxes: tdata.append(( Paragraph(line.description, self.stylesheet['Normal']), "1", localize(line.tax_rate) + " %", money_filter(line.net_value, self.invoice.event.currency), money_filter(line.gross_value, self.invoice.event.currency), )) else: tdata.append(( Paragraph(line.description, self.stylesheet['Normal']), "1", money_filter(line.gross_value, self.invoice.event.currency), )) taxvalue_map[line.tax_rate, line.tax_name] += line.tax_value grossvalue_map[line.tax_rate, line.tax_name] += line.gross_value total += line.gross_value if has_taxes: tdata.append([ pgettext('invoice', 'Invoice total'), '', '', '', money_filter(total, self.invoice.event.currency) ]) colwidths = [a * doc.width for a in (.50, .05, .15, .15, .15)] else: tdata.append([ pgettext('invoice', 'Invoice total'), '', money_filter(total, self.invoice.event.currency) ]) colwidths = [a * doc.width for a in (.65, .05, .30)] if self.invoice.event.settings.invoice_show_payments and not self.invoice.is_cancellation and \ self.invoice.order.status == Order.STATUS_PENDING: pending_sum = self.invoice.order.pending_sum if pending_sum != total: tdata.append([pgettext('invoice', 'Received payments')] + (['', '', ''] if has_taxes else ['']) + [ money_filter(pending_sum - total, self.invoice.event.currency) ]) tdata.append([pgettext('invoice', 'Outstanding payments')] + (['', '', ''] if has_taxes else ['']) + [ money_filter(pending_sum, self.invoice.event.currency) ]) tstyledata += [ ('FONTNAME', (0, len(tdata) - 3), (-1, len(tdata) - 3), self.font_bold), ] table = Table(tdata, colWidths=colwidths, repeatRows=1) table.setStyle(TableStyle(tstyledata)) story.append(table) story.append(Spacer(1, 15 * mm)) if self.invoice.payment_provider_text: story.append(Paragraph(self.invoice.payment_provider_text, self.stylesheet['Normal'])) if self.invoice.additional_text: story.append(Paragraph(self.invoice.additional_text, self.stylesheet['Normal'])) story.append(Spacer(1, 15 * mm)) tstyledata = [ ('ALIGN', (1, 0), (-1, -1), 'RIGHT'), ('LEFTPADDING', (0, 0), (0, -1), 0), ('RIGHTPADDING', (-1, 0), (-1, -1), 0), ('FONTSIZE', (0, 0), (-1, -1), 8), ('FONTNAME', (0, 0), (-1, -1), self.font_regular), ] thead = [ pgettext('invoice', 'Tax rate'), pgettext('invoice', 'Net value'), pgettext('invoice', 'Gross value'), pgettext('invoice', 'Tax'), '' ] tdata = [thead] for idx, gross in grossvalue_map.items(): rate, name = idx if rate == 0: continue tax = taxvalue_map[idx] tdata.append([ localize(rate) + " % " + name, money_filter(gross - tax, self.invoice.event.currency), money_filter(gross, self.invoice.event.currency), money_filter(tax, self.invoice.event.currency), '' ]) def fmt(val): try: return vat_moss.exchange_rates.format(val, self.invoice.foreign_currency_display) except ValueError: return localize(val) + ' ' + self.invoice.foreign_currency_display if len(tdata) > 1 and has_taxes: colwidths = [a * doc.width for a in (.25, .15, .15, .15, .3)] table = Table(tdata, colWidths=colwidths, repeatRows=2, hAlign=TA_LEFT) table.setStyle(TableStyle(tstyledata)) story.append(Spacer(5 * mm, 5 * mm)) story.append(KeepTogether([ Paragraph(pgettext('invoice', 'Included taxes'), self.stylesheet['FineprintHeading']), table ])) if self.invoice.foreign_currency_display and self.invoice.foreign_currency_rate: tdata = [thead] for idx, gross in grossvalue_map.items(): rate, name = idx if rate == 0: continue tax = taxvalue_map[idx] gross = round_decimal(gross * self.invoice.foreign_currency_rate) tax = round_decimal(tax * self.invoice.foreign_currency_rate) net = gross - tax tdata.append([ localize(rate) + " % " + name, fmt(net), fmt(gross), fmt(tax), '' ]) table = Table(tdata, colWidths=colwidths, repeatRows=2, hAlign=TA_LEFT) table.setStyle(TableStyle(tstyledata)) story.append(KeepTogether([ Spacer(1, height=2 * mm), Paragraph( pgettext( 'invoice', 'Using the conversion rate of 1:{rate} as published by the European Central Bank on ' '{date}, this corresponds to:' ).format(rate=localize(self.invoice.foreign_currency_rate), date=date_format(self.invoice.foreign_currency_rate_date, "SHORT_DATE_FORMAT")), self.stylesheet['Fineprint'] ), Spacer(1, height=3 * mm), table ])) elif self.invoice.foreign_currency_display and self.invoice.foreign_currency_rate: foreign_total = round_decimal(total * self.invoice.foreign_currency_rate) story.append(Spacer(1, 5 * mm)) story.append(Paragraph( pgettext( 'invoice', 'Using the conversion rate of 1:{rate} as published by the European Central Bank on ' '{date}, the invoice total corresponds to {total}.' ).format(rate=localize(self.invoice.foreign_currency_rate), date=date_format(self.invoice.foreign_currency_rate_date, "SHORT_DATE_FORMAT"), total=fmt(foreign_total)), self.stylesheet['Fineprint'] )) return story
def get_story(self, doc, form_data): cl = self.event.checkin_lists.get(pk=form_data['list']) questions = list( Question.objects.filter(event=self.event, id__in=form_data['questions'])) headlinestyle = self.get_style() headlinestyle.fontSize = 15 headlinestyle.fontName = 'OpenSansBd' colwidths = [3 * mm, 8 * mm, 8 * mm] + [ a * (doc.width - 8 * mm) for a in [.1, .25, (.25 if questions else .60)] + ([.35 / len(questions)] * len(questions) if questions else []) ] tstyledata = [ ('VALIGN', (0, 0), (-1, 0), 'BOTTOM'), ('ALIGN', (2, 0), (2, 0), 'CENTER'), ('VALIGN', (0, 1), (-1, -1), 'TOP'), ('FONTNAME', (0, 0), (-1, 0), 'OpenSansBd'), ('ALIGN', (0, 0), (0, -1), 'CENTER'), ('TEXTCOLOR', (0, 0), (0, -1), '#990000'), ('FONTNAME', (0, 0), (0, -1), 'OpenSansBd'), ] story = [ Paragraph( '{} – {}'.format(cl.name, (cl.subevent or self.event).get_date_from_display()), headlinestyle), Spacer(1, 5 * mm) ] tdata = [ [ '', '', # Translators: maximum 5 characters TableTextRotate(pgettext('tablehead', 'paid')), _('Order'), _('Name'), _('Product') + '\n' + _('Price'), ], ] headrowstyle = self.get_style() headrowstyle.fontName = 'OpenSansBd' for q in questions: txt = str(q.question) p = Paragraph(txt, headrowstyle) while p.wrap(colwidths[len(tdata[0])], 5000)[1] > 30 * mm: txt = txt[:len(txt) - 50] + "..." p = Paragraph(txt, headrowstyle) tdata[0].append(p) qs = self._get_queryset(cl, form_data) for op in qs: try: ian = op.order.invoice_address.name iac = op.order.invoice_address.company except: ian = "" iac = "" name = op.attendee_name or (op.addon_to.attendee_name if op.addon_to else '') or ian if iac: name += "<br/>" + iac row = [ '!!' if op.item.checkin_attention else '', CBFlowable(bool(op.last_checked_in)), '✘' if op.order.status != Order.STATUS_PAID else '✔', op.order.code, Paragraph(name, self.get_style()), Paragraph( str(op.item) + (" – " + str(op.variation.value) if op.variation else "") + "<br/>" + money_filter(op.price, self.event.currency), self.get_style()), ] acache = {} if op.addon_to: for a in op.addon_to.answers.all(): acache[a.question_id] = str(a) for a in op.answers.all(): acache[a.question_id] = str(a) for q in questions: txt = acache.get(q.pk, '') p = Paragraph(txt, self.get_style()) while p.wrap(colwidths[len(row)], 5000)[1] > 50 * mm: txt = txt[:len(txt) - 50] + "..." p = Paragraph(txt, self.get_style()) row.append(p) if op.order.status != Order.STATUS_PAID: tstyledata += [ ('BACKGROUND', (2, len(tdata)), (2, len(tdata)), '#990000'), ('TEXTCOLOR', (2, len(tdata)), (2, len(tdata)), '#ffffff'), ('ALIGN', (2, len(tdata)), (2, len(tdata)), 'CENTER'), ] tdata.append(row) table = Table(tdata, colWidths=colwidths, repeatRows=1) table.setStyle(TableStyle(tstyledata)) story.append(table) return story