Esempio n. 1
0
def _add_items_to_cart(event: Event,
                       items: List[Tuple[int, Optional[int], int,
                                         Optional[str]]],
                       cart_id: str = None,
                       voucher: str = None) -> None:
    with event.lock():
        _check_date(event)
        existing = CartPosition.objects.filter(
            Q(cart_id=cart_id) & Q(event=event)).count()
        if sum(i[2] for i in items) + existing > int(
                event.settings.max_items_per_order):
            # TODO: i18n plurals
            raise CartError(error_messages['max_items'],
                            (event.settings.max_items_per_order, ))

        expiry = now() + timedelta(
            minutes=event.settings.get('reservation_time', as_type=int))
        _extend_existing(event, cart_id, expiry)

        expired = _re_add_expired_positions(items, event, cart_id)
        if items:
            err = _add_new_items(event, items, cart_id, expiry)
            _delete_expired(expired)
            if err:
                raise CartError(err)
        elif not voucher:
            raise CartError(error_messages['empty'])

        if voucher:
            _add_voucher(event, voucher, expiry, cart_id)
Esempio n. 2
0
def perform_order(event: Event, payment_provider: BasePaymentProvider, positions: list, user: User=None,
                  email: str=None, locale: str=None):
    dt = now()

    try:
        with event.lock():
            check_positions(event, dt, positions)
            order = place_order(event, user, email if user is None else None, positions, dt, payment_provider,
                                locale=locale)
            mail(
                order.email, _('Your order: %(code)s') % {'code': order.code},
                'pretixpresale/email/order_placed.txt',
                {
                    'order': order,
                    'event': event,
                    'url': build_absolute_uri('presale:event.order', kwargs={
                        'event': event.slug,
                        'organizer': event.organizer.slug,
                        'order': order.code,
                    }) + '?order_secret=' + order.secret,
                    'payment': payment_provider.order_pending_mail_render(order)
                },
                event, locale=order.locale
            )
            return order
    except EventLock.LockTimeoutException:
        # Is raised when there are too many threads asking for event locks and we were
        # unable to get one
        raise OrderError(error_messages['busy'])
Esempio n. 3
0
def assign_automatically(event: Event, user_id: int=None, subevent_id: int=None):
    if user_id:
        user = User.objects.get(id=user_id)
    else:
        user = None

    quota_cache = {}
    gone = set()

    qs = WaitingListEntry.objects.filter(
        event=event, voucher__isnull=True
    ).select_related('item', 'variation', 'subevent').prefetch_related(
        'item__quotas', 'variation__quotas'
    ).order_by('-priority', 'created')

    if subevent_id and event.has_subevents:
        subevent = event.subevents.get(id=subevent_id)
        qs = qs.filter(subevent=subevent)

    sent = 0

    with event.lock():
        for wle in qs:
            if (wle.item, wle.variation, wle.subevent) in gone:
                continue

            ev = (wle.subevent or event)
            if not ev.presale_is_running or (wle.subevent and not wle.subevent.active):
                continue
            if wle.subevent and not wle.subevent.presale_is_running:
                continue
            if not wle.item.is_available():
                gone.add((wle.item, wle.variation, wle.subevent))
                continue

            quotas = (wle.variation.quotas.filter(subevent=wle.subevent)
                      if wle.variation
                      else wle.item.quotas.filter(subevent=wle.subevent))
            availability = (
                wle.variation.check_quotas(count_waitinglist=False, _cache=quota_cache, subevent=wle.subevent)
                if wle.variation
                else wle.item.check_quotas(count_waitinglist=False, _cache=quota_cache, subevent=wle.subevent)
            )
            if availability[1] is None or availability[1] > 0:
                try:
                    wle.send_voucher(quota_cache, user=user)
                    sent += 1
                except WaitingListException:  # noqa
                    continue

                # Reduce affected quotas in cache
                for q in quotas:
                    quota_cache[q.pk] = (
                        quota_cache[q.pk][0] if quota_cache[q.pk][0] > 1 else 0,
                        quota_cache[q.pk][1] - 1 if quota_cache[q.pk][1] is not None else sys.maxsize
                    )
            else:
                gone.add((wle.item, wle.variation, wle.subevent))

    return sent
Esempio n. 4
0
def _add_items_to_cart(event: Event, items: List[dict], cart_id: str=None) -> None:
    with event.lock() as now_dt:
        _check_date(event, now_dt)
        existing = CartPosition.objects.filter(Q(cart_id=cart_id) & Q(event=event)).count()
        if sum(i['count'] for i in items) + existing > int(event.settings.max_items_per_order):
            # TODO: i18n plurals
            raise CartError(error_messages['max_items'], (event.settings.max_items_per_order,))

        expiry = now_dt + timedelta(minutes=event.settings.get('reservation_time', as_type=int))
        _extend_existing(event, cart_id, expiry, now_dt)

        expired = _re_add_expired_positions(items, event, cart_id, now_dt)
        if items:
            err = _add_new_items(event, items, cart_id, expiry, now_dt)
            _delete_expired(expired, now_dt)
            if err:
                raise CartError(err)
Esempio n. 5
0
def _add_items_to_cart(event: Event, items: list, session: str=None):
    with event.lock():
        _check_date(event)
        existing = CartPosition.objects.current.filter(Q(session=session) & Q(event=event)).count()
        if sum(i[2] for i in items) + existing > int(event.settings.max_items_per_order):
            # TODO: i18n plurals
            raise CartError(error_messages['max_items'] % event.settings.max_items_per_order)

        expiry = now() + timedelta(minutes=event.settings.get('reservation_time', as_type=int))
        _extend_existing(event, session, expiry)

        expired = _re_add_expired_positions(items, event, session)
        if not items:
            raise CartError(error_messages['empty'])

        err = _add_items(event, items, session, expiry)
        _delete_expired(expired)
        if err:
            raise CartError(err)
Esempio n. 6
0
def _remove_items_from_cart(event: Event, items: List[dict], cart_id: str) -> None:
    with event.lock():
        for i in items:
            cw = Q(cart_id=cart_id) & Q(item_id=i['item']) & Q(event=event)
            if i['variation']:
                cw &= Q(variation_id=i['variation'])
            else:
                cw &= Q(variation__isnull=True)
            # Prefer to delete positions that have the same price as the one the user clicked on, after thet
            # prefer the most expensive ones.
            cnt = i['count']
            if i['price']:
                correctprice = CartPosition.objects.filter(cw).filter(price=Decimal(i['price'].replace(",", ".")))[:cnt]
                for cp in correctprice:
                    cp.delete()
                cnt -= len(correctprice)
            if cnt > 0:
                for cp in CartPosition.objects.filter(cw).order_by("-price")[:cnt]:
                    cp.delete()
Esempio n. 7
0
def _perform_order(event: Event, payment_provider: BasePaymentProvider, position_ids: list,
                   email: str, locale: str):
    event = Event.objects.current.get(identity=event)
    responses = register_payment_providers.send(event)
    pprov = None
    for receiver, response in responses:
        provider = response(event)
        if provider.identifier == payment_provider:
            pprov = provider
    if not pprov:
        raise OrderError(error_messages['internal'])

    dt = now()
    with event.lock():
        positions = list(CartPosition.objects.current.filter(
            identity__in=position_ids).select_related('item', 'variation'))
        if len(position_ids) != len(positions):
            raise OrderError(error_messages['internal'])
        _check_positions(event, dt, positions)
        order = _create_order(event, email, positions, dt, pprov,
                              locale=locale)
        mail(
            order.email, _('Your order: %(code)s') % {'code': order.code},
            'pretixpresale/email/order_placed.txt',
            {
                'order': order,
                'event': event,
                'url': build_absolute_uri('presale:event.order', kwargs={
                    'event': event.slug,
                    'organizer': event.organizer.slug,
                    'order': order.code,
                    'secret': order.secret
                }),
                'payment': pprov.order_pending_mail_render(order)
            },
            event, locale=order.locale
        )
        return order.identity
Esempio n. 8
0
def _add_items_to_cart(event: Event, items: List[Tuple[int, Optional[int], int, Optional[str]]], cart_id: str=None,
                       voucher: str=None) -> None:
    with event.lock():
        _check_date(event)
        existing = CartPosition.objects.filter(Q(cart_id=cart_id) & Q(event=event)).count()
        if sum(i[2] for i in items) + existing > int(event.settings.max_items_per_order):
            # TODO: i18n plurals
            raise CartError(error_messages['max_items'], (event.settings.max_items_per_order,))

        expiry = now() + timedelta(minutes=event.settings.get('reservation_time', as_type=int))
        _extend_existing(event, cart_id, expiry)

        expired = _re_add_expired_positions(items, event, cart_id)
        if items:
            err = _add_new_items(event, items, cart_id, expiry)
            _delete_expired(expired)
            if err:
                raise CartError(err)
        elif not voucher:
            raise CartError(error_messages['empty'])

        if voucher:
            _add_voucher(event, voucher, expiry, cart_id)
Esempio n. 9
0
def _add_items_to_cart(event: Event, items: list, session: str = None):
    with event.lock():
        _check_date(event)
        existing = CartPosition.objects.current.filter(
            Q(session=session) & Q(event=event)).count()
        if sum(i[2] for i in items) + existing > int(
                event.settings.max_items_per_order):
            # TODO: i18n plurals
            raise CartError(error_messages['max_items'] %
                            event.settings.max_items_per_order)

        expiry = now() + timedelta(
            minutes=event.settings.get('reservation_time', as_type=int))
        _extend_existing(event, session, expiry)

        expired = _re_add_expired_positions(items, event, session)
        if not items:
            raise CartError(error_messages['empty'])

        err = _add_items(event, items, session, expiry)
        _delete_expired(expired)
        if err:
            raise CartError(err)
Esempio n. 10
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()
Esempio n. 11
0
def assign_automatically(event: Event,
                         user_id: int = None,
                         subevent_id: int = None):
    if user_id:
        user = User.objects.get(id=user_id)
    else:
        user = None

    quota_cache = {}
    gone = set()
    seats_available = {}

    for m in SeatCategoryMapping.objects.filter(
            event=event).select_related('subevent'):
        # See comment in WaitingListEntry.send_voucher() for rationale
        num_free_seets_for_product = (m.subevent or event).free_seats().filter(
            product_id=m.product_id).count()
        num_valid_vouchers_for_product = event.vouchers.filter(
            Q(valid_until__isnull=True) | Q(valid_until__gte=now()),
            block_quota=True,
            item_id=m.product_id,
            subevent_id=m.subevent_id,
            waitinglistentries__isnull=False).aggregate(
                free=Sum(F('max_usages') - F('redeemed')))['free'] or 0
        seats_available[(
            m.product_id, m.subevent_id
        )] = num_free_seets_for_product - num_valid_vouchers_for_product

    qs = WaitingListEntry.objects.filter(
        event=event, voucher__isnull=True).select_related(
            'item', 'variation', 'subevent').prefetch_related(
                'item__quotas',
                'variation__quotas').order_by('-priority', 'created')

    if subevent_id and event.has_subevents:
        subevent = event.subevents.get(id=subevent_id)
        qs = qs.filter(subevent=subevent)

    sent = 0

    with event.lock():
        for wle in qs:
            if (wle.item, wle.variation, wle.subevent) in gone:
                continue

            ev = (wle.subevent or event)
            if not ev.presale_is_running or (wle.subevent
                                             and not wle.subevent.active):
                continue
            if wle.subevent and not wle.subevent.presale_is_running:
                continue
            if not wle.item.is_available():
                gone.add((wle.item, wle.variation, wle.subevent))
                continue

            if (wle.item_id, wle.subevent_id) in seats_available:
                if seats_available[wle.item_id, wle.subevent_id] < 1:
                    gone.add((wle.item, wle.variation, wle.subevent))
                    continue

            quotas = (wle.variation.quotas.filter(subevent=wle.subevent)
                      if wle.variation else wle.item.quotas.filter(
                          subevent=wle.subevent))
            availability = (wle.variation.check_quotas(count_waitinglist=False,
                                                       _cache=quota_cache,
                                                       subevent=wle.subevent)
                            if wle.variation else wle.item.check_quotas(
                                count_waitinglist=False,
                                _cache=quota_cache,
                                subevent=wle.subevent))
            if availability[1] is None or availability[1] > 0:
                try:
                    wle.send_voucher(quota_cache, user=user)
                    sent += 1
                except WaitingListException:  # noqa
                    continue

                # Reduce affected quotas in cache
                for q in quotas:
                    quota_cache[q.pk] = (quota_cache[q.pk][0]
                                         if quota_cache[q.pk][0] > 1 else 0,
                                         quota_cache[q.pk][1] -
                                         1 if quota_cache[q.pk][1] is not None
                                         else sys.maxsize)

                if (wle.item_id, wle.subevent_id) in seats_available:
                    seats_available[wle.item_id, wle.subevent_id] -= 1
            else:
                gone.add((wle.item, wle.variation, wle.subevent))

    return sent