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)
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'])
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
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)
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)
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()
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
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)
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)
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()
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