def METHOD_NAME(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
    prefetch_related_objects(
        [event.organizer],
        'meta_properties'
    )
    prefetch_related_objects(
        [event],
        Prefetch(
            'meta_values',
            EventMetaValue.objects.select_related('property'),
            to_attr='meta_values_cached'
        )
    )
    qs = event.waitinglistentries.filter(
        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