Example #1
0
def _send_mail(order: Order, subject: LazyI18nString, message: LazyI18nString,
               subevent: SubEvent, refund_amount: Decimal, user: User,
               positions: list):
    with language(order.locale, order.event.settings.region):
        try:
            ia = order.invoice_address
        except InvoiceAddress.DoesNotExist:
            ia = InvoiceAddress(order=order)

        email_context = get_email_context(event_or_subevent=subevent
                                          or order.event,
                                          refund_amount=refund_amount,
                                          order=order,
                                          position_or_address=ia,
                                          event=order.event)
        real_subject = str(subject).format_map(TolerantDict(email_context))
        try:
            order.send_mail(
                real_subject,
                message,
                email_context,
                'pretix.event.order.email.event_canceled',
                user,
            )
        except SendMailException:
            logger.exception('Order canceled email could not be sent')

        for p in positions:
            if subevent and p.subevent_id != subevent.id:
                continue

            if p.addon_to_id is None and p.attendee_email and p.attendee_email != order.email:
                real_subject = str(subject).format_map(
                    TolerantDict(email_context))
                email_context = get_email_context(event_or_subevent=p.subevent
                                                  or order.event,
                                                  event=order.event,
                                                  refund_amount=refund_amount,
                                                  position_or_address=p,
                                                  order=order,
                                                  position=p)
                try:
                    order.send_mail(real_subject,
                                    message,
                                    email_context,
                                    'pretix.event.order.email.event_canceled',
                                    position=p,
                                    user=user)
                except SendMailException:
                    logger.exception(
                        'Order canceled email could not be sent to attendee')
Example #2
0
    def post(self, request, *args, **kwargs):
        if not self.link_form.is_valid():
            messages.error(self.request, _('We had difficulties processing your input.'))
            return self.get(request, *args, **kwargs)

        user = self.link_form.cleaned_data.get('email')

        if settings.HAS_REDIS:
            from django_redis import get_redis_connection
            rc = get_redis_connection("redis")
            if rc.exists('pretix_resend_{}'.format(user)):
                messages.error(request, _('We already sent you an email in the last 24 hours.'))
                return redirect(eventreverse(self.request.event, 'presale:event.resend_link'))
            else:
                rc.setex('pretix_resend_{}'.format(user), 3600 * 24, '1')

        orders = self.request.event.orders.filter(email__iexact=user)

        if not orders:
            user = INVALID_ADDRESS

        subject = _('Your orders for {}').format(self.request.event)
        template = self.request.event.settings.mail_text_resend_all_links
        context = get_email_context(event=self.request.event, orders=orders)
        try:
            mail(user, subject, template, context, event=self.request.event, locale=self.request.LANGUAGE_CODE)
        except SendMailException:
            logger = logging.getLogger('pretix.presale.user')
            logger.exception('A mail resending order links to {} could not be sent.'.format(user))
            messages.error(self.request, _('We have trouble sending emails right now, please check back later.'))
            return self.get(request, *args, **kwargs)

        messages.success(self.request, _('If there were any orders by this user, they will receive an email with their order codes.'))
        return redirect(eventreverse(self.request.event, 'presale:event.index'))
Example #3
0
def twilio_order_message(order, template_name):
    from .tasks import twilio_send

    recipient = order.phone
    if not order.phone:
        return

    context = get_email_context(event=order.event, order=order)
    for k, v in order.event.meta_data.items():
        context["meta_" + k] = v

    with language(order.locale, order.event.settings.region):
        template = order.event.settings.get(f"twilio_text_{template_name}")
        if not str(template):
            return

        try:
            content = render_mail(template, context)
            twilio_send(text=content, to=str(recipient), event=order.event_id)
        except Exception:
            raise
        else:
            order.log_action(
                "pretix_twilio.message.sent",
                data={
                    "message": content,
                    "recipient": str(recipient),
                },
            )
Example #4
0
def send_payment_reminders(sender, **kwargs):
    with scopes_disabled():
        dd = SepaDueDate.objects.filter(
            reminded=False,
            remind_after__lt=now(),
            payment__state=OrderPayment.PAYMENT_STATE_CONFIRMED
        ).select_related(
            'payment',
            'payment__order').prefetch_related('payment__order__event')

        for due_date in dd:
            order = due_date.payment.order
            event = order.event
            subject = event.settings.payment_sepadebit_pre_notification_mail_subject
            text = event.settings.payment_sepadebit_pre_notification_mail_body

            ctx = get_email_context(event=event,
                                    order=order,
                                    sepa_debit_payment=due_date.payment)

            with language(order.locale, event.settings.region):
                due_date.payment.order.send_mail(
                    subject=str(subject),
                    template=text,
                    context=ctx,
                    log_entry_type=
                    'pretix_sepadebit.payment_reminder.sent.order.email')
                due_date.reminded = True
                due_date.save()
Example #5
0
def send_email(self, event, position):
    op = OrderPosition.objects.get(pk=position)
    with language(op.order.locale, event.settings.region):
        email_template = event.settings.cwa_checkin_email_body
        email_subject = str(event.settings.cwa_checkin_email_subject)

        email_context = get_email_context(event=event,
                                          order=op.order,
                                          position=op)
        try:
            if op.attendee_email:
                op.send_mail(
                    email_subject,
                    email_template,
                    email_context,
                    "pretix_cwa.order.position.email.cwa",
                )
            else:
                op.order.send_mail(
                    email_subject,
                    email_template,
                    email_context,
                    "pretix_cwa.order.email.cwa",
                )
        except SendMailException:
            logger.exception("CWA reminder email could not be sent")
Example #6
0
    def send_voucher(self, quota_cache=None, user=None, auth=None):
        availability = (self.variation.check_quotas(count_waitinglist=False,
                                                    subevent=self.subevent,
                                                    _cache=quota_cache)
                        if self.variation else self.item.check_quotas(
                            count_waitinglist=False,
                            subevent=self.subevent,
                            _cache=quota_cache))
        if availability[1] is None or availability[1] < 1:
            raise WaitingListException(
                _('This product is currently not available.'))
        if self.voucher:
            raise WaitingListException(
                _('A voucher has already been sent to this person.'))
        if '@' not in self.email:
            raise WaitingListException(
                _('This entry is anonymized and can no longer be used.'))

        with transaction.atomic():
            v = Voucher.objects.create(
                event=self.event,
                max_usages=1,
                valid_until=now() +
                timedelta(hours=self.event.settings.waiting_list_hours),
                item=self.item,
                variation=self.variation,
                tag='waiting-list',
                comment=_(
                    'Automatically created from waiting list entry for {email}'
                ).format(email=self.email),
                block_quota=True,
                subevent=self.subevent,
            )
            v.log_action(
                'pretix.voucher.added.waitinglist', {
                    'item': self.item.pk,
                    'variation': self.variation.pk if self.variation else None,
                    'tag': 'waiting-list',
                    'block_quota': True,
                    'valid_until': v.valid_until.isoformat(),
                    'max_usages': 1,
                    'email': self.email,
                    'waitinglistentry': self.pk,
                    'subevent': self.subevent.pk if self.subevent else None,
                },
                user=user,
                auth=auth)
            self.log_action('pretix.waitinglist.voucher', user=user, auth=auth)
            self.voucher = v
            self.save()

        with language(self.locale):
            mail(self.email,
                 _('You have been selected from the waitinglist for {event}').
                 format(event=str(self.event)),
                 self.event.settings.mail_text_waiting_list,
                 get_email_context(event=self.event, waiting_list_entry=self),
                 self.event,
                 locale=self.locale)
Example #7
0
def notify_incomplete_payment(o: Order):
    with language(o.locale, o.event.settings.region):
        email_template = o.event.settings.mail_text_order_expire_warning
        email_context = get_email_context(event=o.event, order=o)
        email_subject = gettext('Your order received an incomplete payment: %(code)s') % {'code': o.code}

        try:
            o.send_mail(
                email_subject, email_template, email_context,
                'pretix.event.order.email.expire_warning_sent'
            )
        except SendMailException:
            logger.exception('Reminder email could not be sent')
Example #8
0
def _send_wle_mail(wle: WaitingListEntry, subject: LazyI18nString,
                   message: LazyI18nString, subevent: SubEvent):
    with language(wle.locale, wle.event.settings.region):
        email_context = get_email_context(event_or_subevent=subevent
                                          or wle.event,
                                          event=wle.event)
        try:
            mail(wle.email,
                 str(subject).format_map(TolerantDict(email_context)),
                 message,
                 email_context,
                 wle.event,
                 locale=wle.locale)
        except SendMailException:
            logger.exception('Waiting list canceled email could not be sent')
Example #9
0
def vouchers_send(event: Event, vouchers: list, subject: str, message: str, recipients: list, user: int,
                  progress=None) -> None:
    vouchers = list(Voucher.objects.filter(id__in=vouchers).order_by('id'))
    user = User.objects.get(pk=user)
    for ir, r in enumerate(recipients):
        voucher_list = []
        for i in range(r['number']):
            voucher_list.append(vouchers.pop())
        with language(event.settings.locale):
            email_context = get_email_context(event=event, name=r.get('name') or '',
                                              voucher_list=[v.code for v in voucher_list])
            mail(
                r['email'],
                subject,
                LazyI18nString(message),
                email_context,
                event,
                locale=event.settings.locale,
            )
            logs = []
            for v in voucher_list:
                if r.get('tag') and r.get('tag') != v.tag:
                    v.tag = r.get('tag')
                if v.comment:
                    v.comment += '\n\n'
                v.comment = gettext('The voucher has been sent to {recipient}.').format(recipient=r['email'])
                logs.append(v.log_action(
                    'pretix.voucher.sent',
                    user=user,
                    data={
                        'recipient': r['email'],
                        'name': r.get('name'),
                        'subject': subject,
                        'message': message,
                    },
                    save=False
                ))
            Voucher.objects.bulk_update(voucher_list, fields=['comment', 'tag'], batch_size=500)
            LogEntry.objects.bulk_create(logs, batch_size=500)

            if progress and ir % 50 == 0:
                progress(ir / len(recipients))
def test_mail_context(event, order):
    with scopes_disabled():
        op_date = now().date()
        remind_after = now()
        op = orderpayment(order,
                          date=op_date,
                          reminded=False,
                          remind_after=remind_after,
                          old_format=False)

        ctx = get_email_context(event=event, order=order, sepadebit_payment=op)

        assert ctx['due_date'] == op_date
        assert ctx['account_holder'] == "Testaccount"
        assert ctx['bic'] == "BYLADEM1001"
        assert ctx['iban'] == "DE02xxxx2051"
        assert ctx['reference'] == "TESTREF-123"
        assert ctx['debit_amount'] == "11.00"
        assert ctx['debit_amount_with_currency'] == "€11.00"
        assert ctx[
            'creditor_id'] == event.settings.sepadebit_payment__creditor_id
        assert ctx[
            'creditor_name'] == event.settings.sepadebit_payment__creditor_name
Example #11
0
def vouchers_send(event: Event, vouchers: list, subject: str, message: str,
                  recipients: list, user: int) -> None:
    vouchers = list(Voucher.objects.filter(id__in=vouchers).order_by('id'))
    user = User.objects.get(pk=user)
    for r in recipients:
        voucher_list = []
        for i in range(r['number']):
            voucher_list.append(vouchers.pop())
        with language(event.settings.locale):
            email_context = get_email_context(
                event=event,
                name=r.get('name') or '',
                voucher_list=[v.code for v in voucher_list])
            mail(
                r['email'],
                subject,
                LazyI18nString(message),
                email_context,
                event,
                locale=event.settings.locale,
            )
            for v in voucher_list:
                if r.get('tag') and r.get('tag') != v.tag:
                    v.tag = r.get('tag')
                if v.comment:
                    v.comment += '\n\n'
                v.comment = gettext('The voucher has been sent to {recipient}.'
                                    ).format(recipient=r['email'])
                v.save(update_fields=['tag', 'comment'])
                v.log_action('pretix.voucher.sent',
                             user=user,
                             data={
                                 'recipient': r['email'],
                                 'name': r.get('name'),
                                 'subject': subject,
                                 'message': message,
                             })
Example #12
0
def send_mails(event: Event,
               user: int,
               subject: dict,
               message: dict,
               orders: list,
               items: list,
               recipients: str,
               filter_checkins: bool,
               not_checked_in: bool,
               checkin_lists: list,
               attachments: list = None,
               attach_tickets: bool = False) -> None:
    failures = []
    user = User.objects.get(pk=user) if user else None
    orders = Order.objects.filter(pk__in=orders, event=event)
    subject = LazyI18nString(subject)
    message = LazyI18nString(message)

    for o in orders:
        send_to_order = recipients in ('both', 'orders')

        try:
            ia = o.invoice_address
        except InvoiceAddress.DoesNotExist:
            ia = InvoiceAddress(order=o)

        if recipients in ('both', 'attendees'):
            for p in o.positions.prefetch_related('addons'):
                if p.addon_to_id is not None:
                    continue

                if p.item_id not in items and not any(a.item_id in items
                                                      for a in p.addons.all()):
                    continue

                if filter_checkins:
                    checkins = list(p.checkins.all())
                    allowed = ((not_checked_in and not checkins)
                               or (any(c.list_id in checkin_lists
                                       for c in checkins)))
                    if not allowed:
                        continue

                if not p.attendee_email:
                    if recipients == 'attendees':
                        send_to_order = True
                    continue

                if p.attendee_email == o.email and send_to_order:
                    continue

                try:
                    with language(o.locale, event.settings.region):
                        email_context = get_email_context(
                            event=event,
                            order=o,
                            position_or_address=p,
                            position=p)
                        mail(p.attendee_email,
                             subject,
                             message,
                             email_context,
                             event,
                             locale=o.locale,
                             order=o,
                             position=p,
                             attach_tickets=attach_tickets,
                             attach_cached_files=attachments)
                        o.log_action(
                            'pretix.plugins.sendmail.order.email.sent.attendee',
                            user=user,
                            data={
                                'position':
                                p.positionid,
                                'subject':
                                subject.localize(
                                    o.locale).format_map(email_context),
                                'message':
                                message.localize(
                                    o.locale).format_map(email_context),
                                'recipient':
                                p.attendee_email
                            })
                except SendMailException:
                    failures.append(p.attendee_email)

        if send_to_order and o.email:
            try:
                with language(o.locale, event.settings.region):
                    email_context = get_email_context(event=event,
                                                      order=o,
                                                      position_or_address=ia)
                    mail(o.email,
                         subject,
                         message,
                         email_context,
                         event,
                         locale=o.locale,
                         order=o,
                         attach_tickets=attach_tickets,
                         attach_cached_files=attachments)
                    o.log_action('pretix.plugins.sendmail.order.email.sent',
                                 user=user,
                                 data={
                                     'subject':
                                     subject.localize(
                                         o.locale).format_map(email_context),
                                     'message':
                                     message.localize(
                                         o.locale).format_map(email_context),
                                     'recipient':
                                     o.email
                                 })
            except SendMailException:
                failures.append(o.email)
Example #13
0
    def send(self):
        if self.state not in (ScheduledMail.STATE_SCHEDULED,
                              ScheduledMail.STATE_FAILED):
            raise ValueError("Should not be called in this state")

        e = self.event

        orders = e.orders.all()
        limit_products = self.rule.limit_products.values_list(
            'pk', flat=True) if not self.rule.all_products else None

        if self.subevent:
            orders = orders.filter(
                Exists(
                    OrderPosition.objects.filter(order=OuterRef('pk'),
                                                 subevent=self.subevent)))

        if not self.rule.all_products:
            orders = orders.filter(
                Exists(
                    OrderPosition.objects.filter(order=OuterRef('pk'),
                                                 item_id__in=limit_products)))

        status = [Order.STATUS_PENDING, Order.STATUS_PAID
                  ] if self.rule.include_pending else [Order.STATUS_PAID]

        if self.last_successful_order_id:
            orders = orders.filter(pk__gt=self.last_successful_order_id)

        orders = orders.filter(
            status__in=status,
            require_approval=False,
        ).order_by('pk').select_related('invoice_address').prefetch_related(
            'positions')

        send_to_orders = self.rule.send_to in (Rule.CUSTOMERS, Rule.BOTH)
        send_to_attendees = self.rule.send_to in (Rule.ATTENDEES, Rule.BOTH)

        for o in orders:
            positions = list(o.positions.all())
            o_sent = False

            try:
                ia = o.invoice_address
            except InvoiceAddress.DoesNotExist:
                ia = InvoiceAddress(order=o)

            if send_to_orders and o.email:
                email_ctx = get_email_context(event=e,
                                              order=o,
                                              position_or_address=ia)
                try:
                    o.send_mail(
                        self.rule.subject,
                        self.rule.template,
                        email_ctx,
                        log_entry_type=
                        'pretix.plugins.sendmail.rule.order.email.sent')
                    o_sent = True
                except SendMailException:
                    ...  # ¯\_(ツ)_/¯

            if send_to_attendees:
                if not self.rule.all_products:
                    positions = [
                        p for p in positions if p.item_id in limit_products
                    ]
                if self.subevent_id:
                    positions = [
                        p for p in positions
                        if p.subevent_id == self.subevent_id
                    ]

                for p in positions:
                    email_ctx = get_email_context(event=e,
                                                  order=o,
                                                  position_or_address=ia,
                                                  position=p)
                    try:
                        if p.attendee_email and (p.attendee_email != o.email
                                                 or not o_sent):
                            p.send_mail(
                                self.rule.subject,
                                self.rule.template,
                                email_ctx,
                                log_entry_type=
                                'pretix.plugins.sendmail.rule.order.position.email.sent'
                            )
                        elif not o_sent and o.email:
                            o.send_mail(
                                self.rule.subject,
                                self.rule.template,
                                email_ctx,
                                log_entry_type=
                                'pretix.plugins.sendmail.rule.order.email.sent'
                            )
                            o_sent = True
                    except SendMailException:
                        ...  # ¯\_(ツ)_/¯

            self.last_successful_order_id = o.pk
Example #14
0
def schedule_second_dose(self, event, op):
    op = OrderPosition.objects.select_related("item", "variation", "subevent",
                                              "order").get(pk=op)

    if LinkedOrderPosition.objects.filter(base_position=op).exists():
        return

    itemconf = op.item.vacc_autosched_config
    earliest_date = make_aware(
        datetime.combine(
            op.subevent.date_from.astimezone(event.timezone).date() +
            timedelta(days=itemconf.days),
            op.subevent.date_from.astimezone(event.timezone).time(),
        ),
        event.timezone,
    )

    target_event = itemconf.event or event
    possible_items = [
        n for n in target_event.items.all()
        if (n.internal_name or str(n.name)) == (
            op.item.internal_name or str(op.item.name))
    ]
    if len(possible_items) != 1:
        op.order.log_action(
            "pretix_vacc_autosched.failed",
            data={
                "reason": _("No product found"),
                "position": op.pk,
            },
        )
        return
    target_item = possible_items[0]

    if op.variation or target_item.variations.exists():
        possible_variations = [
            n for n in target_item.variations.all() if str(n.value) == (
                str(op.variation.value) if op.variation else None)
        ]
        if len(possible_variations) != 1:
            op.order.log_action(
                "pretix_vacc_autosched.failed",
                data={
                    "reason": _("No product variation found"),
                    "position": op.pk,
                },
            )
            return
        target_var = possible_variations[0]
    else:
        target_var = None

    for i in range(100):  # max number of subevents to check
        subevent = (target_event.subevents.filter(
            date_from__gte=earliest_date, ).order_by("date_from").first())
        if not subevent:
            op.order.log_action(
                "pretix_vacc_autosched.failed",
                data={
                    "reason": _("No available time slot found"),
                    "position": op.pk,
                },
            )
            return

        try:
            with target_event.lock(), transaction.atomic():
                avcode, avnr = target_item.check_quotas(subevent=subevent,
                                                        fail_on_no_quotas=True)
                if avcode != Quota.AVAILABILITY_OK:
                    # sold out, look for next one
                    earliest_date += timedelta(minutes=1)
                    continue

                childorder = Order.objects.create(
                    event=target_event,
                    status=Order.STATUS_PAID,
                    require_approval=False,
                    testmode=op.order.testmode,
                    email=op.order.email,
                    locale=op.order.locale,
                    expires=now() + timedelta(days=30),
                    total=Decimal("0.00"),
                    expiry_reminder_sent=True,
                    sales_channel=op.order.sales_channel,
                    comment="Auto-generated through scheduling from order {}".
                    format(op.order.code),
                    meta_info=op.order.meta_info,
                )
                op.order.log_action(
                    "pretix_vacc_autosched.scheduled",
                    data={
                        "position": op.pk,
                        "event": target_event.pk,
                        "event_slug": target_event.slug,
                        "order": childorder.code,
                    },
                )
                childorder.log_action(
                    "pretix_vacc_autosched.created",
                    data={
                        "event": event.pk,
                        "event_slug": event.slug,
                        "order": op.order.code,
                    },
                )
                childorder.log_action("pretix.event.order.placed",
                                      data={"source": "vacc_autosched"})
                childpos = childorder.positions.create(
                    positionid=1,
                    tax_rate=Decimal("0.00"),
                    tax_rule=None,
                    tax_value=Decimal("0.00"),
                    subevent=subevent,
                    item=target_item,
                    variation=target_var,
                    price=Decimal("0.00"),
                    attendee_name_cached=op.attendee_name_cached,
                    attendee_name_parts=op.attendee_name_parts,
                    attendee_email=op.attendee_email,
                    company=op.company,
                    street=op.street,
                    zipcode=op.zipcode,
                    city=op.city,
                    country=op.country,
                    state=op.state,
                    addon_to=None,
                    voucher=None,
                    meta_info=op.meta_info,
                )
                for answ in op.answers.all():
                    q = childorder.event.questions.filter(
                        identifier=answ.question.identifier).first()
                    if not q:
                        continue
                    childansw = childpos.answers.create(question=q,
                                                        answer=answ.answer,
                                                        file=answ.file)
                    if answ.options.all():
                        childopts = list(
                            q.options.filter(identifier__in=[
                                o.identifier for o in answ.options.all()
                            ]))
                        if childopts:
                            childansw.options.add(*childopts)

                LinkedOrderPosition.objects.create(base_position=op,
                                                   child_position=childpos)
            order_placed.send(event, order=childorder)
            order_paid.send(event, order=childorder)

            if event.settings.vacc_autosched_mail:
                with language(childorder.locale, target_event.settings.region):
                    email_template = event.settings.vacc_autosched_body
                    email_subject = str(event.settings.vacc_autosched_subject)

                    email_context = get_email_context(event=childorder.event,
                                                      order=childorder)
                    email_context["scheduled_datetime"] = date_format(
                        subevent.date_from.astimezone(event.timezone),
                        "SHORT_DATETIME_FORMAT",
                    )
                    try:
                        childorder.send_mail(
                            email_subject,
                            email_template,
                            email_context,
                            "pretix.event.order.email.order_placed",
                            attach_tickets=True,
                            attach_ical=target_event.settings.mail_attach_ical,
                        )
                    except SendMailException:
                        logger.exception(
                            "Order approved email could not be sent")

        except LockTimeoutException:
            self.retry()

        return

    op.order.log_action(
        "pretix_vacc_autosched.failed",
        data={
            "reason": _("No available time slot found"),
            "position": op.pk,
        },
    )
    return
Example #15
0
def get_private_icals(event, positions):
    """
    Return a list of ical objects based on a sequence of positions.

    Unlike get_public_ical, this will

    - Generate multiple ical files instead of one (but with deduplication applied)
    - Respect the mail_attach_ical_description setting

    It is private in the sense that mail_attach_ical_description may contain content not suited for
    public display.

    We however intentionally do not allow using placeholders based on the order and position
    specifically. This is for two reasons:

    - In reality, many people will add their invite to their calendar which is shared with a larger
      team. People are probably not aware that they're sharing sensitive information such as their
      secret ticket link with everyone they share their calendar with.

    - It would be pretty hard to implement it in a way that doesn't require us to use distinct
      settings fields for emails to customers and to attendees, which feels like an overcomplication.
    """

    from pretix.base.services.mail import TolerantDict

    tz = pytz.timezone(event.settings.timezone)

    creation_time = datetime.datetime.now(pytz.utc)
    calobjects = []

    evs = set(p.subevent or event for p in positions)
    for ev in evs:
        if isinstance(ev, Event):
            url = build_absolute_uri(event, 'presale:event.index')
        else:
            url = build_absolute_uri(event, 'presale:event.index', {
                'subevent': ev.pk
            })

        if event.settings.mail_attach_ical_description:
            ctx = get_email_context(event=event, event_or_subevent=ev)
            description = str(event.settings.mail_attach_ical_description).format_map(TolerantDict(ctx))
        else:
            # Default description
            descr = []
            descr.append(_('Tickets: {url}').format(url=url))
            if ev.date_admission:
                descr.append(str(_('Admission: {datetime}')).format(
                    datetime=date_format(ev.date_admission.astimezone(tz), 'SHORT_DATETIME_FORMAT')
                ))

            descr.append(_('Organizer: {organizer}').format(organizer=event.organizer.name))
            description = '\n'.join(descr)

        cal = vobject.iCalendar()
        cal.add('prodid').value = '-//pretix//{}//'.format(settings.PRETIX_INSTANCE_NAME.replace(" ", "_"))

        vevent = cal.add('vevent')
        vevent.add('summary').value = str(ev.name)
        vevent.add('description').value = description
        vevent.add('dtstamp').value = creation_time
        if ev.location:
            vevent.add('location').value = str(ev.location)

        vevent.add('uid').value = 'pretix-{}-{}-{}@{}'.format(
            event.organizer.slug,
            event.organizer.slug, event.slug,
            ev.pk if not isinstance(ev, Event) else '0',
            urlparse(url).netloc
        )

        if event.settings.show_times:
            vevent.add('dtstart').value = ev.date_from.astimezone(tz)
        else:
            vevent.add('dtstart').value = ev.date_from.astimezone(tz).date()

        if event.settings.show_date_to and ev.date_to:
            if event.settings.show_times:
                vevent.add('dtend').value = ev.date_to.astimezone(tz)
            else:
                # with full-day events date_to in pretix is included (e.g. last day)
                # whereas dtend in vcalendar is non-inclusive => add one day for export
                vevent.add('dtend').value = ev.date_to.astimezone(tz).date() + datetime.timedelta(days=1)

        calobjects.append(cal)
    return calobjects
def book_second_dose(*, op, item, variation, subevent, original_event):
    event = item.event
    with event.lock(), transaction.atomic():
        avcode, avnr = item.check_quotas(subevent=subevent,
                                         fail_on_no_quotas=True)
        if avcode != Quota.AVAILABILITY_OK:
            logger.info(
                f"SECOND DOSE: cannot use slot {subevent.pk}, sold out")
            # sold out, look for next one
            return

        childorder = Order.objects.create(
            event=event,
            status=Order.STATUS_PAID,
            require_approval=False,
            testmode=op.order.testmode,
            email=op.order.email,
            phone=op.order.phone,
            locale=op.order.locale,
            expires=now() + timedelta(days=30),
            total=Decimal("0.00"),
            expiry_reminder_sent=True,
            sales_channel=op.order.sales_channel,
            comment="Auto-generated through scheduling from order {}".format(
                op.order.code),
            meta_info=op.order.meta_info,
        )
        op.order.log_action(
            "pretix_vacc_autosched.scheduled",
            data={
                "position": op.pk,
                "event": event.pk,
                "event_slug": event.slug,
                "order": childorder.code,
            },
        )
        childorder.log_action(
            "pretix_vacc_autosched.created",
            data={
                "event": original_event.pk,
                "event_slug": original_event.slug,
                "order": op.order.code,
            },
        )
        childorder.log_action("pretix.event.order.placed",
                              data={"source": "vacc_autosched"})
        childpos = childorder.positions.create(
            positionid=1,
            tax_rate=Decimal("0.00"),
            tax_rule=None,
            tax_value=Decimal("0.00"),
            subevent=subevent,
            item=item,
            variation=variation,
            price=Decimal("0.00"),
            attendee_name_cached=op.attendee_name_cached,
            attendee_name_parts=op.attendee_name_parts,
            attendee_email=op.attendee_email,
            company=op.company,
            street=op.street,
            zipcode=op.zipcode,
            city=op.city,
            country=op.country,
            state=op.state,
            addon_to=None,
            voucher=None,
            meta_info=op.meta_info,
        )
        for answ in op.answers.all():
            q = childorder.event.questions.filter(
                identifier=answ.question.identifier).first()
            if not q:
                continue
            childansw = childpos.answers.create(question=q,
                                                answer=answ.answer,
                                                file=answ.file)
            if answ.options.all():
                childopts = list(
                    q.options.filter(identifier__in=[
                        o.identifier for o in answ.options.all()
                    ]))
                if childopts:
                    childansw.options.add(*childopts)

        LinkedOrderPosition.objects.create(base_position=op,
                                           child_position=childpos)
        childorder.create_transactions(is_new=True)
    order_placed.send(event, order=childorder)
    order_paid.send(event, order=childorder)

    if original_event.settings.vacc_autosched_mail:
        with language(childorder.locale, original_event.settings.region):
            email_template = original_event.settings.vacc_autosched_body
            email_subject = str(original_event.settings.vacc_autosched_subject)

            email_context = get_email_context(event=childorder.event,
                                              order=childorder)
            email_context["scheduled_datetime"] = date_format(
                subevent.date_from.astimezone(original_event.timezone),
                "SHORT_DATETIME_FORMAT",
            )
            try:
                childorder.send_mail(
                    email_subject,
                    email_template,
                    email_context,
                    "pretix.event.order.email.order_placed",
                    attach_tickets=True,
                    attach_ical=event.settings.mail_attach_ical,
                )
            except SendMailException:
                logger.exception("Order approved email could not be sent")
    if (original_event.settings.vacc_autosched_sms
            and can_use_juvare_api(original_event) and childorder.phone):
        with language(childorder.locale, original_event.settings.region):
            from pretix_juvare_notify.tasks import juvare_send_text

            template = original_event.settings.vacc_autosched_sms_text
            context = get_email_context(event=childorder.event,
                                        order=childorder)
            context["scheduled_datetime"] = date_format(
                subevent.date_from.astimezone(original_event.timezone),
                "SHORT_DATETIME_FORMAT",
            )
            message = str(template).format_map(TolerantDict(context))
            juvare_send_text.apply_async(
                kwargs={
                    "text": message,
                    "to": childorder.phone,
                    "event": original_event.pk,
                })
    logger.info(f"SECOND DOSE: done, created order {childorder.code}")
    return childorder