Example #1
0
    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
Example #2
0
    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
Example #3
0
    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
            ]
        })
Example #4
0
 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)
     }
Example #5
0
 def __str__(self):
     return money_filter(self.value, self.currency)
Example #6
0
     "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)
 }),
Example #7
0
    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
Example #8
0
     "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)
Example #9
0
 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)
Example #10
0
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'], )
Example #11
0
     "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 '')
 }),
Example #12
0
    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
Example #13
0
    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
Example #14
0
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'], )
Example #15
0
    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
Example #16
0
    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
Example #17
0
 def print(self, currency):
     return '{} + {}% = {}'.format(
         money_filter(self.net, currency),
         localize(self.rate),
         money_filter(self.gross, currency)
     )
Example #18
0
    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
Example #19
0
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'],
        )
Example #20
0
     "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 '')
 }),
Example #21
0
 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)
Example #22
0
 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)
Example #23
0
    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,
            })
Example #24
0
 def print(self, currency):
     return '{} + {}% = {}'.format(money_filter(self.net, currency),
                                   localize(self.rate),
                                   money_filter(self.gross, currency))
Example #25
0
    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
Example #26
0
    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