def process_exit_all(sender, **kwargs): qs = CheckinList.objects.filter(exit_all_at__lte=now(), exit_all_at__isnull=False).select_related( 'event', 'event__organizer') for cl in qs: for p in cl.positions_inside: with scope(organizer=cl.event.organizer): ci = Checkin.objects.create(position=p, list=cl, auto_checked_in=True, type=Checkin.TYPE_EXIT, datetime=cl.exit_all_at) checkin_created.send(cl.event, checkin=ci) d = cl.exit_all_at.astimezone(cl.event.timezone) if cl.event.settings.get( f'autocheckin_dst_hack_{cl.pk}' ): # move time back if yesterday was DST switch d -= timedelta(hours=1) cl.event.settings.delete(f'autocheckin_dst_hack_{cl.pk}') try: cl.exit_all_at = make_aware( datetime.combine(d.date() + timedelta(days=1), d.time()), cl.event.timezone) except pytz.exceptions.NonExistentTimeError: cl.event.settings.set(f'autocheckin_dst_hack_{cl.pk}', True) d += timedelta(hours=1) cl.exit_all_at = make_aware( datetime.combine(d.date() + timedelta(days=1), d.time()), cl.event.timezone) # AmbiguousTimeError shouldn't be possible since d.time() includes fold=0 cl.save(update_fields=['exit_all_at'])
def post(self, request, *args, **kwargs): if "can_change_orders" not in request.eventpermset: messages.error(request, _('You do not have permission to perform this action.')) return redirect(reverse('control:event.orders.checkins', kwargs={ 'event': self.request.event.slug, 'organizer': self.request.event.organizer.slug }) + '?' + request.GET.urlencode()) positions = self.get_queryset(filter=False).filter( pk__in=request.POST.getlist('checkin') ) if request.POST.get('revert') == 'true': for op in positions: if op.order.status == Order.STATUS_PAID or (self.list.include_pending and op.order.status == Order.STATUS_PENDING): Checkin.objects.filter(position=op, list=self.list).delete() op.order.log_action('pretix.event.checkin.reverted', data={ 'position': op.id, 'positionid': op.positionid, 'list': self.list.pk, 'web': True }, user=request.user) op.order.touch() messages.success(request, _('The selected check-ins have been reverted.')) else: for op in positions: if op.order.status == Order.STATUS_PAID or (self.list.include_pending and op.order.status == Order.STATUS_PENDING): t = Checkin.TYPE_EXIT if request.POST.get('checkout') == 'true' else Checkin.TYPE_ENTRY if self.list.allow_multiple_entries or t != Checkin.TYPE_ENTRY: ci = Checkin.objects.create(position=op, list=self.list, datetime=now(), type=t) created = True else: ci, created = Checkin.objects.get_or_create(position=op, list=self.list, defaults={ 'datetime': now(), }) op.order.log_action('pretix.event.checkin', data={ 'position': op.id, 'positionid': op.positionid, 'first': created, 'forced': False, 'datetime': now(), 'type': t, 'list': self.list.pk, 'web': True }, user=request.user) checkin_created.send(op.order.event, checkin=ci) messages.success(request, _('The selected tickets have been marked as checked in.')) return redirect(reverse('control:event.orders.checkinlists.show', kwargs={ 'event': self.request.event.slug, 'organizer': self.request.event.organizer.slug, 'list': self.list.pk }) + '?' + request.GET.urlencode())
def order_placed(sender, **kwargs): order = kwargs['order'] event = sender cls = list(event.checkin_lists.filter(auto_checkin_sales_channels__contains=order.sales_channel).prefetch_related( 'limit_products')) if not cls: return for op in order.positions.all(): for cl in cls: if cl.all_products or op.item_id in {i.pk for i in cl.limit_products.all()}: ci = Checkin.objects.create(position=op, list=cl, auto_checked_in=True) checkin_created.send(event, checkin=ci)
def process_exit_all(sender, **kwargs): qs = CheckinList.objects.filter(exit_all_at__lte=now(), exit_all_at__isnull=False).select_related( 'event', 'event__organizer') for cl in qs: for p in cl.positions_inside: with scope(organizer=cl.event.organizer): ci = Checkin.objects.create(position=p, list=cl, auto_checked_in=True, type=Checkin.TYPE_EXIT, datetime=cl.exit_all_at) checkin_created.send(cl.event, checkin=ci) cl.exit_all_at = cl.exit_all_at + timedelta(days=1) cl.save(update_fields=['exit_all_at'])
def perform_checkin(op: OrderPosition, clist: CheckinList, given_answers: dict, force=False, ignore_unpaid=False, nonce=None, datetime=None, questions_supported=True, user=None, auth=None, canceled_supported=False, type=Checkin.TYPE_ENTRY, raw_barcode=None, from_revoked_secret=False): """ Create a checkin for this particular order position and check-in list. Fails with CheckInError if the check in is not valid at this time. :param op: The order position to check in :param clist: The order position to check in :param given_answers: A dictionary of questions mapped to validated, given answers :param force: When set to True, this will succeed even when the position is already checked in or when required questions are not filled out. :param ignore_unpaid: When set to True, this will succeed even when the order is unpaid. :param questions_supported: When set to False, questions are ignored :param nonce: A random nonce to prevent race conditions. :param datetime: The datetime of the checkin, defaults to now. """ dt = datetime or now() if op.canceled or op.order.status not in (Order.STATUS_PAID, Order.STATUS_PENDING): raise CheckInError(_('This order position has been canceled.'), 'canceled' if canceled_supported else 'unpaid') # Do this outside of transaction so it is saved even if the checkin fails for some other reason checkin_questions = list( clist.event.questions.filter(ask_during_checkin=True, items__in=[op.item_id])) require_answers = [] if type != Checkin.TYPE_EXIT and checkin_questions: answers = {a.question: a for a in op.answers.all()} for q in checkin_questions: if q not in given_answers and q not in answers: require_answers.append(q) _save_answers(op, answers, given_answers) with transaction.atomic(): # Lock order positions op = OrderPosition.all.select_for_update().get(pk=op.pk) if not clist.all_products and op.item_id not in [ i.pk for i in clist.limit_products.all() ]: raise CheckInError( _('This order position has an invalid product for this check-in list.' ), 'product') elif clist.subevent_id and op.subevent_id != clist.subevent_id: raise CheckInError( _('This order position has an invalid date for this check-in list.' ), 'product') elif op.order.status != Order.STATUS_PAID and not force and not ( ignore_unpaid and clist.include_pending and op.order.status == Order.STATUS_PENDING): raise CheckInError(_('This order is not marked as paid.'), 'unpaid') if type == Checkin.TYPE_ENTRY and clist.rules and not force: rule_data = LazyRuleVars(op, clist, dt) logic = _get_logic_environment(op.subevent or clist.event) if not logic.apply(clist.rules, rule_data): reason = _logic_explain(clist.rules, op.subevent or clist.event, rule_data) raise CheckInError( _('Entry not permitted: {explanation}.').format( explanation=reason), 'rules', reason=reason) if require_answers and not force and questions_supported: raise RequiredQuestionsError( _('You need to answer questions to complete this check-in.'), 'incomplete', require_answers) device = None if isinstance(auth, Device): device = auth last_cis = list( op.checkins.order_by('-datetime').filter(list=clist).only( 'type', 'nonce')) entry_allowed = (type == Checkin.TYPE_EXIT or clist.allow_multiple_entries or not last_cis or all(c.type == Checkin.TYPE_EXIT for c in last_cis) or (clist.allow_entry_after_exit and last_cis[0].type == Checkin.TYPE_EXIT)) if nonce and ( (last_cis and last_cis[0].nonce == nonce) or op.checkins.filter( type=type, list=clist, device=device, nonce=nonce).exists()): return if entry_allowed or force: ci = Checkin.objects.create( position=op, type=type, list=clist, datetime=dt, device=device, gate=device.gate if device else None, nonce=nonce, forced=force and (not entry_allowed or from_revoked_secret), raw_barcode=raw_barcode, ) op.order.log_action( 'pretix.event.checkin', data={ 'position': op.id, 'positionid': op.positionid, 'first': True, 'forced': force or op.order.status != Order.STATUS_PAID, 'datetime': dt, 'type': type, 'answers': {k.pk: str(v) for k, v in given_answers.items()}, 'list': clist.pk }, user=user, auth=auth) checkin_created.send(op.order.event, checkin=ci) else: raise CheckInError( _('This ticket has already been redeemed.'), 'already_redeemed', )
def perform_checkin(op: OrderPosition, clist: CheckinList, given_answers: dict, force=False, ignore_unpaid=False, nonce=None, datetime=None, questions_supported=True, user=None, auth=None, canceled_supported=False): """ Create a checkin for this particular order position and check-in list. Fails with CheckInError if the check in is not valid at this time. :param op: The order position to check in :param clist: The order position to check in :param given_answers: A dictionary of questions mapped to validated, given answers :param force: When set to True, this will succeed even when the position is already checked in or when required questions are not filled out. :param ignore_unpaid: When set to True, this will succeed even when the order is unpaid. :param questions_supported: When set to False, questions are ignored :param nonce: A random nonce to prevent race conditions. :param datetime: The datetime of the checkin, defaults to now. """ dt = datetime or now() # Fetch order position with related objects op = OrderPosition.all.select_related( 'item', 'variation', 'order', 'addon_to' ).prefetch_related( 'item__questions', Prefetch( 'item__questions', queryset=Question.objects.filter(ask_during_checkin=True), to_attr='checkin_questions' ), 'answers' ).get(pk=op.pk) if op.canceled or op.order.status not in (Order.STATUS_PAID, Order.STATUS_PENDING): raise CheckInError( _('This order position has been canceled.'), 'canceled' if canceled_supported else 'unpaid' ) answers = {a.question: a for a in op.answers.all()} require_answers = [] for q in op.item.checkin_questions: if q not in given_answers and q not in answers: require_answers.append(q) _save_answers(op, answers, given_answers) if not clist.all_products and op.item_id not in [i.pk for i in clist.limit_products.all()]: raise CheckInError( _('This order position has an invalid product for this check-in list.'), 'product' ) elif op.order.status != Order.STATUS_PAID and not force and not ( ignore_unpaid and clist.include_pending and op.order.status == Order.STATUS_PENDING ): raise CheckInError( _('This order is not marked as paid.'), 'unpaid' ) elif require_answers and not force and questions_supported: raise RequiredQuestionsError( _('You need to answer questions to complete this check-in.'), 'incomplete', require_answers ) else: try: ci, created = Checkin.objects.get_or_create(position=op, list=clist, defaults={ 'datetime': dt, 'nonce': nonce, }) except Checkin.MultipleObjectsReturned: ci, created = Checkin.objects.filter(position=op, list=clist).last(), False if created or (nonce and nonce == ci.nonce): if created: op.order.log_action('pretix.event.checkin', data={ 'position': op.id, 'positionid': op.positionid, 'first': True, 'forced': op.order.status != Order.STATUS_PAID, 'datetime': dt, 'list': clist.pk }, user=user, auth=auth) checkin_created.send(op.order.event, checkin=ci) else: if not force: raise CheckInError( _('This ticket has already been redeemed.'), 'already_redeemed', ) op.order.log_action('pretix.event.checkin', data={ 'position': op.id, 'positionid': op.positionid, 'first': False, 'forced': force, 'datetime': dt, 'list': clist.pk }, user=user, auth=auth)