Example #1
0
def mark_order_paid(order: Order, provider: str=None, info: str=None, date: datetime=None, manual: bool=None,
                    force: bool=False, send_mail: bool=True, user: User=None) -> Order:
    """
    Marks an order as paid. This sets the payment provider, info and date and returns
    the order object.

    :param provider: The payment provider that marked this as paid
    :type provider: str
    :param info: The information to store in order.payment_info
    :type info: str
    :param date: The date the payment was received (if you pass ``None``, the current
                 time will be used).
    :type date: datetime
    :param force: Whether this payment should be marked as paid even if no remaining
                  quota is available (default: ``False``).
    :type force: boolean
    :param send_mail: Whether an email should be sent to the user about this event (default: ``True``).
    :type send_mail: boolean
    :param user: The user that performed the change
    :raises Quota.QuotaExceededException: if the quota is exceeded and ``force`` is ``False``
    """
    with order.event.lock() as now_dt:
        can_be_paid = order._can_be_paid()
        if not force and can_be_paid is not True:
            raise Quota.QuotaExceededException(can_be_paid)
        order.payment_provider = provider or order.payment_provider
        order.payment_info = info or order.payment_info
        order.payment_date = date or now_dt
        if manual is not None:
            order.payment_manual = manual
        order.status = Order.STATUS_PAID
        order.save()

    order.log_action('pretix.event.order.paid', {
        'provider': provider,
        'info': info,
        'date': date,
        'manual': manual,
        'force': force
    }, user=user)
    order_paid.send(order.event, order=order)

    if send_mail:
        with language(order.locale):
            mail(
                order.email, _('Payment received for your order: %(code)s') % {'code': order.code},
                order.event.settings.mail_text_order_paid,
                {
                    'event': order.event.name,
                    'url': build_absolute_uri(order.event, 'presale:event.order', kwargs={
                        'order': order.code,
                        'secret': order.secret
                    }),
                    'downloads': order.event.settings.get('ticket_download', as_type=bool)
                },
                order.event, locale=order.locale
            )
    return order
Example #2
0
def mark_order_paid(order: Order, provider: str=None, info: str=None, date: datetime=None, manual: bool=None,
                    force: bool=False, send_mail: bool=True, user: User=None) -> Order:
    """
    Marks an order as paid. This sets the payment provider, info and date and returns
    the order object.

    :param provider: The payment provider that marked this as paid
    :type provider: str
    :param info: The information to store in order.payment_info
    :type info: str
    :param date: The date the payment was received (if you pass ``None``, the current
                 time will be used).
    :type date: datetime
    :param force: Whether this payment should be marked as paid even if no remaining
                  quota is available (default: ``False``).
    :type force: boolean
    :param send_mail: Whether an email should be sent to the user about this event (default: ``True``).
    :type send_mail: boolean
    :param user: The user that performed the change
    :raises Quota.QuotaExceededException: if the quota is exceeded and ``force`` is ``False``
    """
    with order.event.lock():
        can_be_paid = order._can_be_paid()
        if not force and can_be_paid is not True:
            raise Quota.QuotaExceededException(can_be_paid)
        order.payment_provider = provider or order.payment_provider
        order.payment_info = info or order.payment_info
        order.payment_date = date or now()
        if manual is not None:
            order.payment_manual = manual
        order.status = Order.STATUS_PAID
        order.save()

    order.log_action('pretix.event.order.paid', {
        'provider': provider,
        'info': info,
        'date': date,
        'manual': manual,
        'force': force
    }, user=user)
    order_paid.send(order.event, order=order)

    if send_mail:
        with language(order.locale):
            mail(
                order.email, _('Payment received for your order: %(code)s') % {'code': order.code},
                order.event.settings.mail_text_order_paid,
                {
                    'event': order.event.name,
                    'url': build_absolute_uri(order.event, 'presale:event.order', kwargs={
                        'order': order.code,
                        'secret': order.secret
                    }),
                    'downloads': order.event.settings.get('ticket_download', as_type=bool)
                },
                order.event, locale=order.locale
            )
    return order
Example #3
0
def mark_order_paid(order: Order,
                    provider: str = None,
                    info: str = None,
                    date: datetime = None,
                    manual: bool = None,
                    force: bool = False):
    """
    Marks an order as paid. This clones the order object, sets the payment provider,
    info and date and returns the cloned order object.

    :param provider: The payment provider that marked this as paid
    :type provider: str
    :param info: The information to store in order.payment_info
    :type info: str
    :param date: The date the payment was received (if you pass ``None``, the current
                 time will be used).
    :type date: datetime
    :param force: Whether this payment should be marked as paid even if no remaining
                  quota is available (default: ``False``).
    :type force: boolean
    :raises Quota.QuotaExceededException: if the quota is exceeded and ``force`` is ``False``
    """
    with order.event.lock():
        can_be_paid = order._can_be_paid()
        if not force and can_be_paid is not True:
            raise Quota.QuotaExceededException(can_be_paid)
        order = order.clone()
        order.payment_provider = provider or order.payment_provider
        order.payment_info = info or order.payment_info
        order.payment_date = date or now()
        if manual is not None:
            order.payment_manual = manual
        order.status = Order.STATUS_PAID
        order.save()
        order_paid.send(order.event, order=order)

    mail(order.email,
         _('Payment received for your order: %(code)s') % {'code': order.code},
         'pretixpresale/email/order_paid.txt', {
             'order':
             order,
             'event':
             order.event,
             'url':
             build_absolute_uri(order.event,
                                'presale:event.order',
                                kwargs={
                                    'order': order.code,
                                    'secret': order.secret
                                }),
             'downloads':
             order.event.settings.get('ticket_download', as_type=bool)
         },
         order.event,
         locale=order.locale)
    return order
Example #4
0
def mark_order_paid(order, provider=None, info=None, date=None, manual=None, force=False):
    """
    Marks an order as paid. This clones the order object, sets the payment provider,
    info and date and returns the cloned order object.

    :param provider: The payment provider that marked this as paid
    :type provider: str
    :param info: The information to store in order.payment_info
    :type info: str
    :param date: The date the payment was received (if you pass ``None``, the current
                 time will be used).
    :type date: datetime
    :param force: Whether this payment should be marked as paid even if no remaining
                  quota is available (default: ``False``).
    :type force: boolean
    :raises Quota.QuotaExceededException: if the quota is exceeded and ``force`` is ``False``
    """
    can_be_paid, quotas_locked = order._can_be_paid(keep_locked=True)
    if not force and can_be_paid is not True:
        raise Quota.QuotaExceededException(can_be_paid)
    order = order.clone()
    order.payment_provider = provider or order.payment_provider
    order.payment_info = info or order.payment_info
    order.payment_date = date or now()
    if manual is not None:
        order.payment_manual = manual
    order.status = Order.STATUS_PAID
    order.save()
    order_paid.send(order.event, order=order)

    if quotas_locked:
        for quota in quotas_locked:
            quota.release()

    from pretix.base.services.mail import mail

    mail(
        order.user, _('Payment received for your order: %(code)s') % {'code': order.code},
        'pretixpresale/email/order_paid.txt',
        {
            'user': order.user,
            'order': order,
            'event': order.event,
            'url': build_absolute_uri('presale:event.order', kwargs={
                'event': order.event.slug,
                'organizer': order.event.organizer.slug,
                'order': order.code,
            }),
            'downloads': order.event.settings.get('ticket_download', as_type=bool)
        },
        order.event
    )
    return order
Example #5
0
def mark_order_paid(order: Order, provider: str=None, info: str=None, date: datetime=None, manual: bool=None,
                    force: bool=False, send_mail: bool=True, user: User=None, mail_text='') -> Order:
    """
    Marks an order as paid. This sets the payment provider, info and date and returns
    the order object.

    :param provider: The payment provider that marked this as paid
    :type provider: str
    :param info: The information to store in order.payment_info
    :type info: str
    :param date: The date the payment was received (if you pass ``None``, the current
                 time will be used).
    :type date: datetime
    :param force: Whether this payment should be marked as paid even if no remaining
                  quota is available (default: ``False``).
    :type force: boolean
    :param send_mail: Whether an email should be sent to the user about this event (default: ``True``).
    :type send_mail: boolean
    :param user: The user that performed the change
    :param mail_text: Additional text to be included in the email
    :type mail_text: str
    :raises Quota.QuotaExceededException: if the quota is exceeded and ``force`` is ``False``
    """
    if order.status == Order.STATUS_PAID:
        return order

    with order.event.lock() as now_dt:
        can_be_paid = order._can_be_paid()
        if not force and can_be_paid is not True:
            raise Quota.QuotaExceededException(can_be_paid)
        order.payment_provider = provider or order.payment_provider
        order.payment_info = info or order.payment_info
        order.payment_date = date or now_dt
        if manual is not None:
            order.payment_manual = manual
        order.status = Order.STATUS_PAID
        order.save()

    order.log_action('pretix.event.order.paid', {
        'provider': provider,
        'info': info,
        'date': date or now_dt,
        'manual': manual,
        'force': force
    }, user=user)
    order_paid.send(order.event, order=order)

    if order.event.settings.get('invoice_generate') in ('True', 'paid') and invoice_qualified(order):
        if not order.invoices.exists():
            generate_invoice(order)

    if send_mail:
        with language(order.locale):
            try:
                invoice_name = order.invoice_address.name
                invoice_company = order.invoice_address.company
            except InvoiceAddress.DoesNotExist:
                invoice_name = ""
                invoice_company = ""
            email_template = order.event.settings.mail_text_order_paid
            email_context = {
                'event': order.event.name,
                'url': build_absolute_uri(order.event, 'presale:event.order', kwargs={
                    'order': order.code,
                    'secret': order.secret
                }),
                'downloads': order.event.settings.get('ticket_download', as_type=bool),
                'invoice_name': invoice_name,
                'invoice_company': invoice_company,
                'payment_info': mail_text
            }
            email_subject = _('Payment received for your order: %(code)s') % {'code': order.code}
            try:
                order.send_mail(
                    email_subject, email_template, email_context,
                    'pretix.event.order.email.order_paid', user
                )
            except SendMailException:
                logger.exception('Order paid email could not be sent')

    return order
Example #6
0
def import_orders(event: Event, fileid: str, settings: dict, locale: str,
                  user) -> None:
    # TODO: quotacheck?
    cf = CachedFile.objects.get(id=fileid)
    user = User.objects.get(pk=user)
    with language(locale, event.settings.region):
        cols = get_all_columns(event)
        parsed = parse_csv(cf.file)
        orders = []
        order = None
        data = []

        # Run validation
        for i, record in enumerate(parsed):
            if not any(record.values()):
                continue
            values = {}
            for c in cols:
                val = c.resolve(settings, record)
                if isinstance(val, str):
                    val = val.strip()
                try:
                    values[c.identifier] = c.clean(val, values)
                except ValidationError as e:
                    raise DataImportError(
                        _('Error while importing value "{value}" for column "{column}" in line "{line}": {message}'
                          ).format(value=val if val is not None else '',
                                   column=c.verbose_name,
                                   line=i + 1,
                                   message=e.message))
            data.append(values)

        # Prepare model objects. Yes, this might consume lots of RAM, but allows us to make the actual SQL transaction
        # shorter. We'll see what works better in reality…
        for i, record in enumerate(data):
            try:
                if order is None or settings['orders'] == 'many':
                    order = Order(
                        event=event,
                        testmode=settings['testmode'],
                    )
                    order.meta_info = {}
                    order._positions = []
                    order._address = InvoiceAddress()
                    order._address.name_parts = {
                        '_scheme': event.settings.name_scheme
                    }
                    orders.append(order)

                position = OrderPosition(positionid=len(order._positions) + 1)
                position.attendee_name_parts = {
                    '_scheme': event.settings.name_scheme
                }
                position.meta_info = {}
                order._positions.append(position)
                position.assign_pseudonymization_id()

                for c in cols:
                    c.assign(record.get(c.identifier), order, position,
                             order._address)

            except ImportError as e:
                raise ImportError(
                    _('Invalid data in row {row}: {message}').format(
                        row=i, message=str(e)))

        # quota check?
        with event.lock():
            with transaction.atomic():
                save_transactions = []
                for o in orders:
                    o.total = sum([c.price for c in o._positions
                                   ])  # currently no support for fees
                    if o.total == Decimal('0.00'):
                        o.status = Order.STATUS_PAID
                        o.save()
                        OrderPayment.objects.create(
                            local_id=1,
                            order=o,
                            amount=Decimal('0.00'),
                            provider='free',
                            info='{}',
                            payment_date=now(),
                            state=OrderPayment.PAYMENT_STATE_CONFIRMED)
                    elif settings['status'] == 'paid':
                        o.status = Order.STATUS_PAID
                        o.save()
                        OrderPayment.objects.create(
                            local_id=1,
                            order=o,
                            amount=o.total,
                            provider='manual',
                            info='{}',
                            payment_date=now(),
                            state=OrderPayment.PAYMENT_STATE_CONFIRMED)
                    else:
                        o.status = Order.STATUS_PENDING
                        o.save()
                    for p in o._positions:
                        p.order = o
                        p.save()
                    o._address.order = o
                    o._address.save()
                    for c in cols:
                        c.save(o)
                    o.log_action('pretix.event.order.placed',
                                 user=user,
                                 data={'source': 'import'})
                    save_transactions += o.create_transactions(
                        is_new=True,
                        fees=[],
                        positions=o._positions,
                        save=False)
                Transaction.objects.bulk_create(save_transactions)

            for o in orders:
                with language(o.locale, event.settings.region):
                    order_placed.send(event, order=o)
                    if o.status == Order.STATUS_PAID:
                        order_paid.send(event, order=o)

                    gen_invoice = invoice_qualified(o) and (
                        (event.settings.get('invoice_generate') == 'True') or
                        (event.settings.get('invoice_generate') == 'paid'
                         and o.status
                         == Order.STATUS_PAID)) and not o.invoices.last()
                    if gen_invoice:
                        generate_invoice(o, trigger_pdf=True)
    cf.delete()
Example #7
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
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