コード例 #1
0
ファイル: main.py プロジェクト: MrGirlyMan/pretix
class EventCreate(OrganizerPermissionRequiredMixin, CreateView):
    model = Event
    form_class = EventCreateForm
    template_name = 'pretixcontrol/events/create.html'
    context_object_name = 'event'
    permission = 'can_create_events'

    @cached_property
    def sform(self):
        return EventCreateSettingsForm(
            obj=Event(),
            prefix='settings',
            data=self.request.POST if self.request.method == 'POST' else None
        )

    def post(self, request, *args, **kwargs):
        form = self.get_form()
        if form.is_valid() and self.sform.is_valid():
            return self.form_valid(form)
        else:
            return self.form_invalid(form)

    def get_context_data(self, *args, **kwargs) -> dict:
        context = super().get_context_data(*args, **kwargs)
        context['sform'] = self.sform
        return context

    def dispatch(self, request, *args, **kwargs):
        self.object = Event()
        return super().dispatch(request, *args, **kwargs)

    def get_form_kwargs(self):
        kwargs = super().get_form_kwargs()
        kwargs['organizer'] = self.request.organizer
        return kwargs

    @transaction.atomic
    def form_valid(self, form):
        messages.success(self.request, _('The new event has been created.'))
        form.instance.organizer = self.request.organizer
        ret = super().form_valid(form)
        EventPermission.objects.create(
            event=form.instance, user=self.request.user,
        )
        self.object = form.instance
        self.object.plugins = settings.PRETIX_PLUGINS_DEFAULT
        self.object.save()

        self.sform.obj = form.instance
        self.sform.save()
        form.instance.log_action('pretix.event.settings', user=self.request.user, data={
            k: form.instance.settings.get(k) for k in self.sform.changed_data
        })
        return ret

    def get_success_url(self) -> str:
        return reverse('control:event.settings', kwargs={
            'organizer': self.request.organizer.slug,
            'event': self.object.slug,
        })
コード例 #2
0
class EventCreate(OrganizerPermissionRequiredMixin, CreateView):
    model = Event
    form_class = EventCreateForm
    template_name = 'pretixcontrol/events/create.html'
    context_object_name = 'event'
    permission = 'can_create_events'

    @cached_property
    def sform(self):
        return EventCreateSettingsForm(
            obj=Event(),
            prefix='settings',
            data=self.request.POST if self.request.method == 'POST' else None
        )

    def post(self, request, *args, **kwargs):
        form = self.get_form()
        if form.is_valid() and self.sform.is_valid():
            return self.form_valid(form)
        else:
            return self.form_invalid(form)

    def get_context_data(self, *args, **kwargs) -> dict:
        context = super().get_context_data(*args, **kwargs)
        context['sform'] = self.sform
        return context

    def dispatch(self, request, *args, **kwargs):
        self.object = Event()
        return super().dispatch(request, *args, **kwargs)

    def get_form_kwargs(self):
        kwargs = super().get_form_kwargs()
        kwargs['organizer'] = self.request.organizer
        return kwargs

    @transaction.atomic
    def form_valid(self, form):
        messages.success(self.request, _('The new event has been created.'))
        form.instance.organizer = self.request.organizer
        ret = super().form_valid(form)
        EventPermission.objects.create(
            event=form.instance, user=self.request.user,
        )
        self.object = form.instance
        self.object.plugins = settings.PRETIX_PLUGINS_DEFAULT
        self.object.save()

        self.sform.obj = form.instance
        self.sform.save()
        form.instance.log_action('pretix.event.settings', user=self.request.user, data={
            k: form.instance.settings.get(k) for k in self.sform.changed_data
        })
        return ret

    def get_success_url(self) -> str:
        return reverse('control:event.settings', kwargs={
            'organizer': self.request.organizer.slug,
            'event': self.object.slug,
        })
コード例 #3
0
    def test_presale_end_before_start(self):
        event = Event(
            organizer=self.organizer, name='Dummy', slug='dummy',
            presale_start=now(), presale_end=now() - timedelta(hours=1)
        )
        with self.assertRaises(ValidationError) as context:
            event.clean()

        self.assertIn('presale_end', str(context.exception))
コード例 #4
0
    def test_slug_validation(self):
        event = Event(
            organizer=self.organizer, name='Download', slug='download',
            date_from=datetime.datetime(2013, 12, 26, tzinfo=datetime.timezone.utc)
        )
        with self.assertRaises(ValidationError) as context:
            event.full_clean()

        self.assertIn('slug', str(context.exception))
コード例 #5
0
    def validate(self, data):
        data = super().validate(data)

        full_data = self.to_internal_value(self.to_representation(self.instance)) if self.instance else {}
        full_data.update(data)

        Event.clean_dates(data.get('date_from'), data.get('date_to'))
        Event.clean_presale(data.get('presale_start'), data.get('presale_end'))

        return data
コード例 #6
0
ファイル: event.py プロジェクト: FlaviaBastos/pretix
    def validate(self, data):
        data = super().validate(data)

        full_data = self.to_internal_value(self.to_representation(self.instance)) if self.instance else {}
        full_data.update(data)

        Event.clean_dates(data.get('date_from'), data.get('date_to'))
        Event.clean_presale(data.get('presale_start'), data.get('presale_end'))

        return data
コード例 #7
0
ファイル: test_event.py プロジェクト: chotee/pretix
    def test_slug_validation(self):
        event = Event(organizer=self.orga,
                      name='download',
                      slug='download',
                      date_from=datetime.datetime(
                          2013, 12, 26, tzinfo=datetime.timezone.utc),
                      live=True)
        with self.assertRaises(ValidationError):
            if event.full_clean():
                event.save()

        self.assertEqual(Event.objects.filter(name='download').count(), 0)
コード例 #8
0
    def validate(self, data):
        data = super().validate(data)
        event = self.context['request'].event

        full_data = self.to_internal_value(self.to_representation(self.instance)) if self.instance else {}
        full_data.update(data)

        Event.clean_dates(data.get('date_from'), data.get('date_to'))
        Event.clean_presale(data.get('presale_start'), data.get('presale_end'))

        SubEvent.clean_items(event, [item['item'] for item in full_data.get('subeventitem_set', [])])
        SubEvent.clean_variations(event, [item['variation'] for item in full_data.get('subeventitemvariation_set', [])])
        return data
コード例 #9
0
ファイル: event.py プロジェクト: astrocbxy/pretix
    def validate(self, data):
        data = super().validate(data)

        full_data = self.to_internal_value(self.to_representation(self.instance)) if self.instance else {}
        full_data.update(data)

        Event.clean_dates(data.get('date_from'), data.get('date_to'))
        Event.clean_presale(data.get('presale_start'), data.get('presale_end'))

        if full_data.get('has_subevents') and full_data.get('seating_plan'):
            raise ValidationError('Event series should not directly be assigned a seating plan.')

        return data
コード例 #10
0
ファイル: event.py プロジェクト: astrocbxy/pretix
    def validate(self, data):
        data = super().validate(data)
        event = self.context['request'].event

        full_data = self.to_internal_value(self.to_representation(self.instance)) if self.instance else {}
        full_data.update(data)

        Event.clean_dates(data.get('date_from'), data.get('date_to'))
        Event.clean_presale(data.get('presale_start'), data.get('presale_end'))

        SubEvent.clean_items(event, [item['item'] for item in full_data.get('subeventitem_set', [])])
        SubEvent.clean_variations(event, [item['variation'] for item in full_data.get('subeventitemvariation_set', [])])
        return data
コード例 #11
0
ファイル: test_event.py プロジェクト: rixx/pretix
    def test_slug_validation(self):
        event = Event(
            organizer=self.orga,
            name='download',
            slug='download',
            date_from=datetime.datetime(2013, 12, 26, tzinfo=datetime.timezone.utc),
            live=True
        )
        with self.assertRaises(ValidationError):
            if event.full_clean():
                event.save()

        self.assertEqual(Event.objects.filter(name='download').count(), 0)
コード例 #12
0
 def _get_event_queryset(self):
     query = Q(is_public=True) & Q(live=True)
     qs = self.request.organizer.events.using(settings.DATABASE_REPLICA).filter(query)
     qs = qs.annotate(
         min_from=Min('subevents__date_from'),
         min_to=Min('subevents__date_to'),
         max_from=Max('subevents__date_from'),
         max_to=Max('subevents__date_to'),
         max_fromto=Greatest(Max('subevents__date_to'), Max('subevents__date_from')),
     )
     if "old" in self.request.GET:
         qs = qs.filter(
             Q(Q(has_subevents=False) & Q(
                 Q(date_to__lt=now()) | Q(Q(date_to__isnull=True) & Q(date_from__lt=now()))
             )) | Q(Q(has_subevents=True) & Q(
                 Q(min_to__lt=now()) | Q(min_from__lt=now()))
             )
         ).annotate(
             order_to=Coalesce('max_fromto', 'max_to', 'max_from', 'date_to', 'date_from'),
         ).order_by('-order_to')
     else:
         qs = qs.filter(
             Q(Q(has_subevents=False) & Q(
                 Q(date_to__gte=now()) | Q(Q(date_to__isnull=True) & Q(date_from__gte=now()))
             )) | Q(Q(has_subevents=True) & Q(
                 Q(max_to__gte=now()) | Q(max_from__gte=now()))
             )
         ).annotate(
             order_from=Coalesce('min_from', 'date_from'),
         ).order_by('order_from')
     qs = Event.annotated(filter_qs_by_attr(qs, self.request))
     return qs
コード例 #13
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
コード例 #14
0
ファイル: organizer.py プロジェクト: thorstenEURESA/pretix
 def _events_by_day(self, before, after):
     ebd = defaultdict(list)
     timezones = set()
     add_events_for_days(
         self.request,
         Event.annotated(self.request.organizer.events,
                         'web').using(settings.DATABASE_REPLICA).filter(
                             sales_channels__contains=self.request.
                             sales_channel.identifier), before, after, ebd,
         timezones)
     add_subevents_for_days(
         filter_qs_by_attr(
             SubEvent.annotated(
                 SubEvent.objects.filter(
                     event__organizer=self.request.organizer,
                     event__is_public=True,
                     event__live=True,
                     event__sales_channels__contains=self.request.
                     sales_channel.identifier).prefetch_related(
                         'event___settings_objects',
                         'event__organizer___settings_objects')),
             self.request).using(settings.DATABASE_REPLICA), before, after,
         ebd, timezones)
     self._multiple_timezones = len(timezones) > 1
     return ebd
コード例 #15
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)
コード例 #16
0
def shred(event: Event, fileid: str, confirm_code: str) -> None:
    known_shredders = event.get_data_shredders()
    try:
        cf = CachedFile.objects.get(pk=fileid)
    except CachedFile.DoesNotExist:
        raise ShredError(
            _("The download file could no longer be found on the server, please try to start again."
              ))
    with ZipFile(cf.file.file, 'r') as zipfile:
        indexdata = json.loads(zipfile.read('index.json').decode())
    if indexdata['organizer'] != event.organizer.slug or indexdata[
            'event'] != event.slug:
        raise ShredError(_("This file is from a different event."))
    if indexdata['confirm_code'] != confirm_code:
        raise ShredError(_("The confirm code you entered was incorrect."))
    if event.logentry_set.filter(datetime__gte=parse(indexdata['time'])):
        raise ShredError(
            _("Something happened in your event after the export, please try again."
              ))

    for s in indexdata['shredders']:
        shredder = known_shredders.get(s)
        if not shredder:
            continue

        shredder.shred_data()

    cf.file.delete(save=False)
    cf.delete()
コード例 #17
0
ファイル: organizer.py プロジェクト: InnerImmolation/pretix
 def _get_event_queryset(self):
     query = Q(is_public=True) & Q(live=True)
     qs = self.request.organizer.events.using(settings.DATABASE_REPLICA).filter(query)
     qs = qs.annotate(
         min_from=Min('subevents__date_from'),
         min_to=Min('subevents__date_to'),
         max_from=Max('subevents__date_from'),
         max_to=Max('subevents__date_to'),
         max_fromto=Greatest(Max('subevents__date_to'), Max('subevents__date_from')),
     )
     if "old" in self.request.GET:
         qs = qs.filter(
             Q(Q(has_subevents=False) & Q(
                 Q(date_to__lt=now()) | Q(Q(date_to__isnull=True) & Q(date_from__lt=now()))
             )) | Q(Q(has_subevents=True) & Q(
                 Q(min_to__lt=now()) | Q(min_from__lt=now()))
             )
         ).annotate(
             order_to=Coalesce('max_fromto', 'max_to', 'max_from', 'date_to', 'date_from'),
         ).order_by('-order_to')
     else:
         qs = qs.filter(
             Q(Q(has_subevents=False) & Q(
                 Q(date_to__gte=now()) | Q(Q(date_to__isnull=True) & Q(date_from__gte=now()))
             )) | Q(Q(has_subevents=True) & Q(
                 Q(max_to__gte=now()) | Q(max_from__gte=now()))
             )
         ).annotate(
             order_from=Coalesce('min_from', 'date_from'),
         ).order_by('order_from')
     qs = Event.annotated(filter_qs_by_attr(qs, self.request))
     return qs
コード例 #18
0
ファイル: orders.py プロジェクト: ilmjstrope/pretix
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'])
コード例 #19
0
ファイル: views.py プロジェクト: pbotiast/pretix
 def set_initial_from_event(self, event: Event):
     banktransfer = event.get_payment_providers(
         cached=True)[BankTransfer.identifier]
     self.initial["account_holder"] = banktransfer.settings.get(
         "bank_details_sepa_name")
     self.initial["iban"] = banktransfer.settings.get(
         "bank_details_sepa_iban")
     self.initial["bic"] = banktransfer.settings.get(
         "bank_details_sepa_bic")
コード例 #20
0
ファイル: organizer.py プロジェクト: ryardley/pretix
 def _events_by_day(self, before, after):
     ebd = defaultdict(list)
     timezones = set()
     add_events_for_days(self.request, Event.annotated(self.request.organizer.events, 'web'), before, after, ebd, timezones)
     add_subevents_for_days(filter_qs_by_attr(SubEvent.annotated(SubEvent.objects.filter(
         event__organizer=self.request.organizer,
         event__is_public=True,
         event__live=True,
     ).prefetch_related(
         'event___settings_objects', 'event__organizer___settings_objects'
     )), self.request), before, after, ebd, timezones)
     self._multiple_timezones = len(timezones) > 1
     return ebd
コード例 #21
0
 def _events_by_day(self, before, after):
     ebd = defaultdict(list)
     timezones = set()
     add_events_for_days(self.request, Event.annotated(self.request.organizer.events, 'web').using(settings.DATABASE_REPLICA), before, after, ebd, timezones)
     add_subevents_for_days(filter_qs_by_attr(SubEvent.annotated(SubEvent.objects.filter(
         event__organizer=self.request.organizer,
         event__is_public=True,
         event__live=True,
     ).prefetch_related(
         'event___settings_objects', 'event__organizer___settings_objects'
     )), self.request).using(settings.DATABASE_REPLICA), before, after, ebd, timezones)
     self._multiple_timezones = len(timezones) > 1
     return ebd
コード例 #22
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)
コード例 #23
0
ファイル: shredder.py プロジェクト: thorstenEURESA/pretix
def export(event: Event, shredders: List[str], session_key=None) -> None:
    known_shredders = event.get_data_shredders()

    with NamedTemporaryFile() as rawfile:
        with ZipFile(rawfile, 'w') as zipfile:
            ccode = get_random_string(6)
            zipfile.writestr(
                'CONFIRM_CODE.txt',
                ccode,
            )
            zipfile.writestr(
                'index.json',
                json.dumps(
                    {
                        'instance': settings.SITE_URL,
                        'organizer': event.organizer.slug,
                        'event': event.slug,
                        'time': now().isoformat(),
                        'shredders': shredders,
                        'confirm_code': ccode
                    },
                    indent=4))
            for s in shredders:
                shredder = known_shredders.get(s)
                if not shredder:
                    continue

                it = shredder.generate_files()
                if not it:
                    continue
                for fname, ftype, content in it:
                    zipfile.writestr(fname, content)

        rawfile.seek(0)

        cf = CachedFile()
        cf.date = now()
        cf.filename = event.slug + '.zip'
        cf.type = 'application/zip'
        cf.session_key = session_key
        cf.web_download = True
        cf.expires = now() + timedelta(hours=1)
        cf.save()
        cf.file.save(cachedfile_name(cf, cf.filename), rawfile)

    return cf.pk
コード例 #24
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()
コード例 #25
0
ファイル: cart.py プロジェクト: simoneott/pretix
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)
コード例 #26
0
ファイル: orders.py プロジェクト: simoneott/pretix
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
コード例 #27
0
ファイル: cart.py プロジェクト: cygery/pretix
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)
コード例 #28
0
ファイル: cart.py プロジェクト: theafricanengineer/pretix
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)
コード例 #29
0
    def get_queryset(self):
        if isinstance(self.request.auth, (TeamAPIToken, Device)):
            qs = self.request.auth.get_events_with_any_permission()
        elif self.request.user.is_authenticated:
            qs = self.request.user.get_events_with_any_permission(
                self.request).filter(organizer=self.request.organizer)

        qs = filter_qs_by_attr(qs, self.request)

        if 'with_availability_for' in self.request.GET:
            qs = Event.annotated(
                qs, channel=self.request.GET.get('with_availability_for'))

        return qs.prefetch_related(
            'organizer',
            'meta_values',
            'meta_values__property',
            'item_meta_properties',
            Prefetch(
                'seat_category_mappings',
                to_attr='_seat_category_mappings',
                queryset=SeatCategoryMapping.objects.filter(subevent=None)),
        )
コード例 #30
0
 def dispatch(self, request, *args, **kwargs):
     self.object = Event()
     return super().dispatch(request, *args, **kwargs)
コード例 #31
0
ファイル: event.py プロジェクト: astrocbxy/pretix
 def validate_slug(self, value):
     Event.clean_slug(self.context['request'].organizer, self.instance, value)
     return value
コード例 #32
0
ファイル: event.py プロジェクト: astrocbxy/pretix
 def validate_has_subevents(self, value):
     Event.clean_has_subevents(self.instance, value)
     return value
コード例 #33
0
    def _get_event_list(self, request, **kwargs):
        data = {}
        o = getattr(request, 'event', request.organizer)
        list_type = self.request.GET.get("style", o.settings.event_list_type)
        data['list_type'] = list_type

        cache_key = ':'.join([
            'widget.py',
            'eventlist',
            request.organizer.slug,
            request.event.slug if hasattr(request, 'event') else '-',
            list_type,
            request.GET.get("year") or "-",
            request.GET.get("month") or "-",
            request.GET.get("old") or "-",
            get_language(),
        ])
        cached_data = cache.get(cache_key)
        if cached_data:
            return self.response(cached_data)

        if list_type == "calendar":
            self._set_month_year()
            _, ndays = calendar.monthrange(self.year, self.month)

            data['date'] = date(self.year, self.month, 1)
            if hasattr(self.request, 'event'):
                tz = pytz.timezone(self.request.event.settings.timezone)
            else:
                tz = pytz.UTC
            before = datetime(self.year, self.month, 1, 0, 0, 0, tzinfo=tz) - timedelta(days=1)
            after = datetime(self.year, self.month, ndays, 0, 0, 0, tzinfo=tz) + timedelta(days=1)

            ebd = defaultdict(list)

            if hasattr(self.request, 'event'):
                add_subevents_for_days(
                    self.request.event.subevents_annotated('web'),
                    before, after, ebd, set(), self.request.event,
                    kwargs.get('cart_namespace')
                )
            else:
                timezones = set()
                add_events_for_days(self.request, Event.annotated(self.request.organizer.events, 'web'), before, after, ebd, timezones)
                add_subevents_for_days(filter_qs_by_attr(SubEvent.annotated(SubEvent.objects.filter(
                    event__organizer=self.request.organizer,
                    event__is_public=True,
                    event__live=True,
                ).prefetch_related(
                    'event___settings_objects', 'event__organizer___settings_objects'
                )), self.request), before, after, ebd, timezones)

            data['weeks'] = weeks_for_template(ebd, self.year, self.month)
            for w in data['weeks']:
                for d in w:
                    if not d:
                        continue
                    d['events'] = self._serialize_events(d['events'] or [])
        else:
            if hasattr(self.request, 'event'):
                evs = self.request.event.subevents_sorted(
                    self.request.event.subevents_annotated(self.request.sales_channel)
                )
                tz = pytz.timezone(request.event.settings.timezone)
                data['events'] = [
                    {
                        'name': str(ev.name),
                        'date_range': ev.get_date_range_display(tz) + (
                            (" " + ev.get_time_from_display(tz)) if ev.event.settings.show_times else ""
                        ),
                        'availability': self._get_availability(ev, ev.event),
                        'event_url': build_absolute_uri(ev.event, 'presale:event.index'),
                        'subevent': ev.pk,
                    } for ev in evs
                ]
            else:
                data['events'] = []
                qs = self._get_event_queryset()
                for event in qs:
                    tz = pytz.timezone(event.cache.get_or_set('timezone', lambda: event.settings.timezone))
                    if event.has_subevents:
                        dr = daterange(
                            event.min_from.astimezone(tz),
                            (event.max_fromto or event.max_to or event.max_from).astimezone(tz)
                        )
                        avail = {'color': 'none', 'text': ugettext('Event series')}
                    else:
                        dr = event.get_date_range_display(tz) + (
                            " " + event.get_time_from_display(tz) if event.settings.show_times else ""
                        )
                        avail = self._get_availability(event, event)
                    data['events'].append({
                        'name': str(event.name),
                        'date_range': dr,
                        'availability': avail,
                        'event_url': build_absolute_uri(event, 'presale:event.index'),
                    })

        cache.set(cache_key, data, 30)
        # These pages are cached for a really short duration – this should make them pretty accurate, while still
        # providing some protection against burst traffic.
        return self.response(data)
コード例 #34
0
ファイル: main.py プロジェクト: cherti/pretix
 def dispatch(self, request, *args, **kwargs):
     self.object = Event()
     return super().dispatch(request, *args, **kwargs)
コード例 #35
0
def mail(email: str, subject: str, template: Union[str, LazyI18nString],
         context: Dict[str, Any]=None, event: Event=None, locale: str=None,
         order: Order=None, position: OrderPosition=None, headers: dict=None, sender: str=None,
         invoices: list=None, attach_tickets=False, auto_email=True, user=None, attach_ical=False):
    """
    Sends out an email to a user. The mail will be sent synchronously or asynchronously depending on the installation.

    :param email: The email address of the recipient

    :param subject: The email subject. Should be localized to the recipients's locale or a lazy object that will be
        localized by being casted to a string.

    :param template: The filename of a template to be used. It will be rendered with the locale given in the locale
        argument and the context given in the next argument. Alternatively, you can pass a LazyI18nString and
        ``context`` will be used as the argument to a  Python ``.format_map()`` call on the template.

    :param context: The context for rendering the template (see ``template`` parameter)

    :param event: The event this email is related to (optional). If set, this will be used to determine the sender,
        a possible prefix for the subject and the SMTP server that should be used to send this email.

    :param order: The order this email is related to (optional). If set, this will be used to include a link to the
        order below the email.

    :param order: The order position this email is related to (optional). If set, this will be used to include a link
        to the order position instead of the order below the email.

    :param headers: A dict of custom mail headers to add to the mail

    :param locale: The locale to be used while evaluating the subject and the template

    :param sender: Set the sender email address. If not set and ``event`` is set, the event's default will be used,
        otherwise the system default.

    :param invoices: A list of invoices to attach to this email.

    :param attach_tickets: Whether to attach tickets to this email, if they are available to download.

    :param attach_ical: Whether to attach relevant ``.ics`` files to this email

    :param auto_email: Whether this email is auto-generated

    :param user: The user this email is sent to

    :raises MailOrderException: on obvious, immediate failures. Not raising an exception does not necessarily mean
        that the email has been sent, just that it has been queued by the email backend.
    """
    if email == INVALID_ADDRESS:
        return

    headers = headers or {}
    if auto_email:
        headers['X-Auto-Response-Suppress'] = 'OOF, NRN, AutoReply, RN'
        headers['Auto-Submitted'] = 'auto-generated'

    with language(locale):
        if isinstance(context, dict) and event:
            for k, v in event.meta_data.items():
                context['meta_' + k] = v

        if isinstance(context, dict) and order:
            try:
                context.update({
                    'invoice_name': order.invoice_address.name,
                    'invoice_company': order.invoice_address.company
                })
            except InvoiceAddress.DoesNotExist:
                context.update({
                    'invoice_name': '',
                    'invoice_company': ''
                })
        renderer = ClassicMailRenderer(None)
        content_plain = body_plain = render_mail(template, context)
        subject = str(subject).format_map(TolerantDict(context))
        sender = sender or (event.settings.get('mail_from') if event else settings.MAIL_FROM)
        if event:
            sender_name = event.settings.mail_from_name or str(event.name)
            sender = formataddr((sender_name, sender))
        else:
            sender = formataddr((settings.PRETIX_INSTANCE_NAME, sender))

        subject = str(subject)
        signature = ""

        bcc = []
        if event:
            renderer = event.get_html_mail_renderer()
            if event.settings.mail_bcc:
                for bcc_mail in event.settings.mail_bcc.split(','):
                    bcc.append(bcc_mail.strip())

            if event.settings.mail_from == settings.DEFAULT_FROM_EMAIL and event.settings.contact_mail and not headers.get('Reply-To'):
                headers['Reply-To'] = event.settings.contact_mail

            prefix = event.settings.get('mail_prefix')
            if prefix and prefix.startswith('[') and prefix.endswith(']'):
                prefix = prefix[1:-1]
            if prefix:
                subject = "[%s] %s" % (prefix, subject)

            body_plain += "\r\n\r\n-- \r\n"

            signature = str(event.settings.get('mail_text_signature'))
            if signature:
                signature = signature.format(event=event.name)
                body_plain += signature
                body_plain += "\r\n\r\n-- \r\n"

            if order and order.testmode:
                subject = "[TESTMODE] " + subject

            if order and position:
                body_plain += _(
                    "You are receiving this email because someone placed an order for {event} for you."
                ).format(event=event.name)
                body_plain += "\r\n"
                body_plain += _(
                    "You can view your order details at the following URL:\n{orderurl}."
                ).replace("\n", "\r\n").format(
                    event=event.name, orderurl=build_absolute_uri(
                        order.event, 'presale:event.order.position', kwargs={
                            'order': order.code,
                            'secret': position.web_secret,
                            'position': position.positionid,
                        }
                    )
                )
            elif order:
                body_plain += _(
                    "You are receiving this email because you placed an order for {event}."
                ).format(event=event.name)
                body_plain += "\r\n"
                body_plain += _(
                    "You can view your order details at the following URL:\n{orderurl}."
                ).replace("\n", "\r\n").format(
                    event=event.name, orderurl=build_absolute_uri(
                        order.event, 'presale:event.order.open', kwargs={
                            'order': order.code,
                            'secret': order.secret,
                            'hash': order.email_confirm_hash()
                        }
                    )
                )
            body_plain += "\r\n"

        try:
            if 'position' in inspect.signature(renderer.render).parameters:
                body_html = renderer.render(content_plain, signature, str(subject), order, position)
            else:
                # Backwards compatibility
                warnings.warn('E-mail renderer called without position argument because position argument is not '
                              'supported.',
                              DeprecationWarning)
                body_html = renderer.render(content_plain, signature, str(subject), order)
        except:
            logger.exception('Could not render HTML body')
            body_html = None

        send_task = mail_send_task.si(
            to=[email],
            bcc=bcc,
            subject=subject,
            body=body_plain,
            html=body_html,
            sender=sender,
            event=event.id if event else None,
            headers=headers,
            invoices=[i.pk for i in invoices] if invoices and not position else [],
            order=order.pk if order else None,
            position=position.pk if position else None,
            attach_tickets=attach_tickets,
            attach_ical=attach_ical,
            user=user.pk if user else None
        )

        if invoices:
            task_chain = [invoice_pdf_task.si(i.pk).on_error(send_task) for i in invoices if not i.file]
        else:
            task_chain = []

        task_chain.append(send_task)
        chain(*task_chain).apply_async()
コード例 #36
0
ファイル: stats.py プロジェクト: FlaviaBastos/pretix
def order_overview(event: Event, subevent: SubEvent=None) -> Tuple[List[Tuple[ItemCategory, List[Item]]],
                                                                   Dict[str, Tuple[Decimal, Decimal]]]:
    items = event.items.all().select_related(
        'category',  # for re-grouping
    ).prefetch_related(
        'variations'
    ).order_by('category__position', 'category_id', 'position', 'name')

    qs = OrderPosition.objects
    if subevent:
        qs = qs.filter(subevent=subevent)
    counters = qs.filter(
        order__event=event
    ).values(
        'item', 'variation', 'order__status'
    ).annotate(cnt=Count('id'), price=Sum('price'), tax_value=Sum('tax_value')).order_by()

    states = {
        'canceled': Order.STATUS_CANCELED,
        'refunded': Order.STATUS_REFUNDED,
        'paid': Order.STATUS_PAID,
        'pending': Order.STATUS_PENDING,
        'expired': Order.STATUS_EXPIRED,
    }
    num = {}
    for l, s in states.items():
        num[l] = {
            (p['item'], p['variation']): (p['cnt'], p['price'], p['price'] - p['tax_value'])
            for p in counters if p['order__status'] == s
        }

    num['total'] = dictsum(num['pending'], num['paid'])

    for item in items:
        item.all_variations = list(item.variations.all())
        item.has_variations = (len(item.all_variations) > 0)
        item.num = {}
        if item.has_variations:
            for var in item.all_variations:
                variid = var.id
                var.num = {}
                for l in states.keys():
                    var.num[l] = num[l].get((item.id, variid), (0, 0, 0))
                var.num['total'] = num['total'].get((item.id, variid), (0, 0, 0))
            for l in states.keys():
                item.num[l] = tuplesum(var.num[l] for var in item.all_variations)
            item.num['total'] = tuplesum(var.num['total'] for var in item.all_variations)
        else:
            for l in states.keys():
                item.num[l] = num[l].get((item.id, None), (0, 0, 0))
            item.num['total'] = num['total'].get((item.id, None), (0, 0, 0))

    nonecat = ItemCategory(name=_('Uncategorized'))
    # Regroup those by category
    items_by_category = sorted(
        [
            # a group is a tuple of a category and a list of items
            (cat if cat is not None else nonecat, [i for i in items if i.category == cat])
            for cat in set([i.category for i in items])
            # insert categories into a set for uniqueness
            # a set is unsorted, so sort again by category
        ],
        key=lambda group: (group[0].position, group[0].id) if (
            group[0] is not None and group[0].id is not None) else (0, 0)
    )

    for c in items_by_category:
        c[0].num = {}
        for l in states.keys():
            c[0].num[l] = tuplesum(item.num[l] for item in c[1])
        c[0].num['total'] = tuplesum(item.num['total'] for item in c[1])

    # Payment fees
    payment_cat_obj = DummyObject()
    payment_cat_obj.name = _('Fees')
    payment_items = []

    if not subevent:
        counters = OrderFee.objects.filter(
            order__event=event
        ).values(
            'fee_type', 'internal_type', 'order__status'
        ).annotate(cnt=Count('id'), value=Sum('value'), tax_value=Sum('tax_value')).order_by()

        for l, s in states.items():
            num[l] = {
                (o['fee_type'], o['internal_type']): (o['cnt'], o['value'], o['value'] - o['tax_value'])
                for o in counters if o['order__status'] == s
            }
        num['total'] = dictsum(num['pending'], num['paid'])

        provider_names = {
            k: v.verbose_name
            for k, v in event.get_payment_providers().items()
        }
        names = dict(OrderFee.FEE_TYPES)

        for pprov, total in sorted(num['total'].items(), key=lambda i: i[0]):
            ppobj = DummyObject()
            if pprov[0] == OrderFee.FEE_TYPE_PAYMENT:
                ppobj.name = '{} - {}'.format(names[pprov[0]], provider_names.get(pprov[1], pprov[1]))
            else:
                name = pprov[1]
                for r, resp in order_fee_type_name.send(sender=event, fee_type=pprov[0], internal_type=pprov[1]):
                    if resp:
                        name = resp
                        break

                ppobj.name = '{} - {}'.format(names[pprov[0]], name)
            ppobj.provider = pprov[1]
            ppobj.has_variations = False
            ppobj.num = {}
            for l in states.keys():
                ppobj.num[l] = num[l].get(pprov, (0, 0, 0))
            ppobj.num['total'] = total
            payment_items.append(ppobj)

        payment_cat_obj.num = {}
        for l in states.keys():
            payment_cat_obj.num[l] = (
                Dontsum(''), sum(i.num[l][1] for i in payment_items), sum(i.num[l][2] for i in payment_items)
            )
        payment_cat_obj.num['total'] = (
            Dontsum(''), sum(i.num['total'][1] for i in payment_items), sum(i.num['total'][2] for i in payment_items)
        )
        payment_cat = (payment_cat_obj, payment_items)
        any_payment = any(payment_cat_obj.num[s][1] for s in states.keys())
        if any_payment:
            items_by_category.append(payment_cat)

    total = {
        'num': {'total': tuplesum(c.num['total'] for c, i in items_by_category)}
    }
    for l in states.keys():
        total['num'][l] = tuplesum(c.num[l] for c, i in items_by_category)

    return items_by_category, total
コード例 #37
0
ファイル: mail.py プロジェクト: FlaviaBastos/pretix
def mail(email: str, subject: str, template: Union[str, LazyI18nString],
         context: Dict[str, Any]=None, event: Event=None, locale: str=None,
         order: Order=None, headers: dict=None, sender: str=None, invoices: list=None,
         attach_tickets=False):
    """
    Sends out an email to a user. The mail will be sent synchronously or asynchronously depending on the installation.

    :param email: The email address of the recipient

    :param subject: The email subject. Should be localized to the recipients's locale or a lazy object that will be
        localized by being casted to a string.

    :param template: The filename of a template to be used. It will be rendered with the locale given in the locale
        argument and the context given in the next argument. Alternatively, you can pass a LazyI18nString and
        ``context`` will be used as the argument to a  Python ``.format_map()`` call on the template.

    :param context: The context for rendering the template (see ``template`` parameter)

    :param event: The event this email is related to (optional). If set, this will be used to determine the sender,
        a possible prefix for the subject and the SMTP server that should be used to send this email.

    :param order: The order this email is related to (optional). If set, this will be used to include a link to the
        order below the email.

    :param headers: A dict of custom mail headers to add to the mail

    :param locale: The locale to be used while evaluating the subject and the template

    :param sender: Set the sender email address. If not set and ``event`` is set, the event's default will be used,
        otherwise the system default.

    :param invoices: A list of invoices to attach to this email.

    :param attach_tickets: Whether to attach tickets to this email, if they are available to download.

    :raises MailOrderException: on obvious, immediate failures. Not raising an exception does not necessarily mean
        that the email has been sent, just that it has been queued by the email backend.
    """
    if email == INVALID_ADDRESS:
        return

    headers = headers or {}

    with language(locale):
        if isinstance(context, dict) and order:
            try:
                context.update({
                    'invoice_name': order.invoice_address.name,
                    'invoice_company': order.invoice_address.company
                })
            except InvoiceAddress.DoesNotExist:
                context.update({
                    'invoice_name': '',
                    'invoice_company': ''
                })
        renderer = ClassicMailRenderer(None)
        content_plain = body_plain = render_mail(template, context)
        subject = str(subject).format_map(context)
        sender = sender or (event.settings.get('mail_from') if event else settings.MAIL_FROM)
        if event:
            sender = formataddr((str(event.name), sender))
        else:
            sender = formataddr((settings.PRETIX_INSTANCE_NAME, sender))

        subject = str(subject)
        signature = ""

        bcc = []
        if event:
            renderer = event.get_html_mail_renderer()
            if event.settings.mail_bcc:
                bcc.append(event.settings.mail_bcc)

            if event.settings.mail_from == settings.DEFAULT_FROM_EMAIL and event.settings.contact_mail and not headers.get('Reply-To'):
                headers['Reply-To'] = event.settings.contact_mail

            prefix = event.settings.get('mail_prefix')
            if prefix and prefix.startswith('[') and prefix.endswith(']'):
                prefix = prefix[1:-1]
            if prefix:
                subject = "[%s] %s" % (prefix, subject)

            body_plain += "\r\n\r\n-- \r\n"

            signature = str(event.settings.get('mail_text_signature'))
            if signature:
                signature = signature.format(event=event.name)
                body_plain += signature
                body_plain += "\r\n\r\n-- \r\n"

            if order:
                body_plain += _(
                    "You are receiving this email because you placed an order for {event}."
                ).format(event=event.name)
                body_plain += "\r\n"
                body_plain += _(
                    "You can view your order details at the following URL:\n{orderurl}."
                ).replace("\n", "\r\n").format(
                    event=event.name, orderurl=build_absolute_uri(
                        order.event, 'presale:event.order', kwargs={
                            'order': order.code,
                            'secret': order.secret
                        }
                    )
                )
            body_plain += "\r\n"

        try:
            body_html = renderer.render(content_plain, signature, str(subject), order)
        except:
            logger.exception('Could not render HTML body')
            body_html = None

        send_task = mail_send_task.si(
            to=[email],
            bcc=bcc,
            subject=subject,
            body=body_plain,
            html=body_html,
            sender=sender,
            event=event.id if event else None,
            headers=headers,
            invoices=[i.pk for i in invoices] if invoices else [],
            order=order.pk if order else None,
            attach_tickets=attach_tickets
        )

        if invoices:
            task_chain = [invoice_pdf_task.si(i.pk).on_error(send_task) for i in invoices if not i.file]
        else:
            task_chain = []

        task_chain.append(send_task)
        chain(*task_chain).apply_async()
コード例 #38
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()
コード例 #39
0
def cancel_event(self,
                 event: Event,
                 subevent: int,
                 auto_refund: bool,
                 keep_fee_fixed: str,
                 keep_fee_per_ticket: str,
                 keep_fee_percentage: str,
                 keep_fees: list = None,
                 manual_refund: bool = False,
                 send: bool = False,
                 send_subject: dict = None,
                 send_message: dict = None,
                 send_waitinglist: bool = False,
                 send_waitinglist_subject: dict = {},
                 send_waitinglist_message: dict = {},
                 user: int = None,
                 refund_as_giftcard: bool = False,
                 giftcard_expires=None,
                 giftcard_conditions=None,
                 subevents_from: str = None,
                 subevents_to: str = None):
    send_subject = LazyI18nString(send_subject)
    send_message = LazyI18nString(send_message)
    send_waitinglist_subject = LazyI18nString(send_waitinglist_subject)
    send_waitinglist_message = LazyI18nString(send_waitinglist_message)
    if user:
        user = User.objects.get(pk=user)

    s = OrderPosition.objects.filter(
        order=OuterRef('pk')).order_by().values('order').annotate(
            k=Count('id')).values('k')
    orders_to_cancel = event.orders.annotate(
        pcnt=Subquery(s, output_field=IntegerField())).filter(
            status__in=[
                Order.STATUS_PAID, Order.STATUS_PENDING, Order.STATUS_EXPIRED
            ],
            pcnt__gt=0).all()

    if subevent or subevents_from:
        if subevent:
            subevents = event.subevents.filter(pk=subevent)
            subevent = subevents.first()
            subevent_ids = {subevent.pk}
        else:
            subevents = event.subevents.filter(date_from__gte=subevents_from,
                                               date_from__lt=subevents_to)
            subevent_ids = set(subevents.values_list('id', flat=True))

        has_subevent = OrderPosition.objects.filter(
            order_id=OuterRef('pk')).filter(subevent__in=subevents)
        has_other_subevent = OrderPosition.objects.filter(
            order_id=OuterRef('pk')).exclude(subevent__in=subevents)
        orders_to_change = orders_to_cancel.annotate(
            has_subevent=Exists(has_subevent),
            has_other_subevent=Exists(has_other_subevent),
        ).filter(has_subevent=True, has_other_subevent=True)
        orders_to_cancel = orders_to_cancel.annotate(
            has_subevent=Exists(has_subevent),
            has_other_subevent=Exists(has_other_subevent),
        ).filter(has_subevent=True, has_other_subevent=False)

        for se in subevents:
            se.log_action(
                'pretix.subevent.canceled',
                user=user,
            )
            se.active = False
            se.save(update_fields=['active'])
            se.log_action('pretix.subevent.changed',
                          user=user,
                          data={
                              'active': False,
                              '_source': 'cancel_event'
                          })
    else:
        subevents = None
        subevent_ids = set()
        orders_to_change = event.orders.none()
        event.log_action(
            'pretix.event.canceled',
            user=user,
        )

        for i in event.items.filter(active=True):
            i.active = False
            i.save(update_fields=['active'])
            i.log_action('pretix.event.item.changed',
                         user=user,
                         data={
                             'active': False,
                             '_source': 'cancel_event'
                         })
    failed = 0
    total = orders_to_cancel.count() + orders_to_change.count()
    qs_wl = event.waitinglistentries.filter(
        voucher__isnull=True).select_related('subevent')
    if subevents:
        qs_wl = qs_wl.filter(subevent__in=subevents)
    if send_waitinglist:
        total += qs_wl.count()
    counter = 0
    self.update_state(state='PROGRESS', meta={'value': 0})

    for o in orders_to_cancel.only('id', 'total').iterator():
        try:
            fee = Decimal('0.00')
            fee_sum = Decimal('0.00')
            keep_fee_objects = []
            if keep_fees:
                for f in o.fees.all():
                    if f.fee_type in keep_fees:
                        fee += f.value
                        keep_fee_objects.append(f)
                    fee_sum += f.value
            if keep_fee_percentage:
                fee += Decimal(keep_fee_percentage) / Decimal('100.00') * (
                    o.total - fee_sum)
            if keep_fee_fixed:
                fee += Decimal(keep_fee_fixed)
            if keep_fee_per_ticket:
                for p in o.positions.all():
                    if p.addon_to_id is None:
                        fee += min(p.price, Decimal(keep_fee_per_ticket))
            fee = round_decimal(min(fee, o.payment_refund_sum), event.currency)

            _cancel_order(o.pk,
                          user,
                          send_mail=False,
                          cancellation_fee=fee,
                          keep_fees=keep_fee_objects)
            refund_amount = o.payment_refund_sum

            try:
                if auto_refund:
                    _try_auto_refund(o.pk,
                                     manual_refund=manual_refund,
                                     allow_partial=True,
                                     source=OrderRefund.REFUND_SOURCE_ADMIN,
                                     refund_as_giftcard=refund_as_giftcard,
                                     giftcard_expires=giftcard_expires,
                                     giftcard_conditions=giftcard_conditions,
                                     comment=gettext('Event canceled'))
            finally:
                if send:
                    _send_mail(o, send_subject, send_message, subevent,
                               refund_amount, user, o.positions.all())

            counter += 1
            if not self.request.called_directly and counter % max(
                    10, total // 100) == 0:
                self.update_state(
                    state='PROGRESS',
                    meta={
                        'value': round(counter / total * 100 if total else 0,
                                       2)
                    })
        except LockTimeoutException:
            logger.exception("Could not cancel order")
            failed += 1
        except OrderError:
            logger.exception("Could not cancel order")
            failed += 1

    for o in orders_to_change.values_list('id', flat=True).iterator():
        with transaction.atomic():
            o = event.orders.select_for_update().get(pk=o)
            total = Decimal('0.00')
            fee = Decimal('0.00')
            positions = []

            ocm = OrderChangeManager(o, user=user, notify=False)
            for p in o.positions.all():
                if p.subevent_id in subevent_ids:
                    total += p.price
                    ocm.cancel(p)
                    positions.append(p)

                    if keep_fee_per_ticket:
                        if p.addon_to_id is None:
                            fee += min(p.price, Decimal(keep_fee_per_ticket))

            if keep_fee_fixed:
                fee += Decimal(keep_fee_fixed)
            if keep_fee_percentage:
                fee += Decimal(keep_fee_percentage) / Decimal('100.00') * total
            fee = round_decimal(min(fee, o.payment_refund_sum), event.currency)
            if fee:
                f = OrderFee(
                    fee_type=OrderFee.FEE_TYPE_CANCELLATION,
                    value=fee,
                    order=o,
                    tax_rule=o.event.settings.tax_rate_default,
                )
                f._calculate_tax()
                ocm.add_fee(f)

            ocm.commit()
            refund_amount = o.payment_refund_sum - o.total

            if auto_refund:
                _try_auto_refund(o.pk,
                                 manual_refund=manual_refund,
                                 allow_partial=True,
                                 source=OrderRefund.REFUND_SOURCE_ADMIN,
                                 refund_as_giftcard=refund_as_giftcard,
                                 giftcard_expires=giftcard_expires,
                                 giftcard_conditions=giftcard_conditions,
                                 comment=gettext('Event canceled'))

            if send:
                _send_mail(o, send_subject, send_message, subevent,
                           refund_amount, user, positions)

            counter += 1
            if not self.request.called_directly and counter % max(
                    10, total // 100) == 0:
                self.update_state(
                    state='PROGRESS',
                    meta={
                        'value': round(counter / total * 100 if total else 0,
                                       2)
                    })

    if send_waitinglist:
        for wle in qs_wl:
            _send_wle_mail(wle, send_waitinglist_subject,
                           send_waitinglist_message, wle.subevent)

            counter += 1
            if not self.request.called_directly and counter % max(
                    10, total // 100) == 0:
                self.update_state(
                    state='PROGRESS',
                    meta={
                        'value': round(counter / total * 100 if total else 0,
                                       2)
                    })
    return failed
コード例 #40
0
ファイル: event.py プロジェクト: FlaviaBastos/pretix
 def validate_slug(self, value):
     Event.clean_slug(self.context['request'].organizer, self.instance, value)
     return value
コード例 #41
0
ファイル: event.py プロジェクト: FlaviaBastos/pretix
 def validate_has_subevents(self, value):
     Event.clean_has_subevents(self.instance, value)
     return value
コード例 #42
0
def order_overview(
    event: Event,
    subevent: SubEvent = None
) -> Tuple[List[Tuple[ItemCategory, List[Item]]], Dict[str, Tuple[Decimal,
                                                                  Decimal]]]:
    items = event.items.all().select_related(
        'category',  # for re-grouping
    ).prefetch_related('variations').order_by('category__position',
                                              'category_id', 'position',
                                              'name')

    qs = OrderPosition.objects
    if subevent:
        qs = qs.filter(subevent=subevent)
    counters = qs.filter(order__event=event).values(
        'item', 'variation',
        'order__status').annotate(cnt=Count('id'),
                                  price=Sum('price'),
                                  tax_value=Sum('tax_value')).order_by()

    num_canceled = {(p['item'], p['variation']):
                    (p['cnt'], p['price'], p['price'] - p['tax_value'])
                    for p in counters
                    if p['order__status'] == Order.STATUS_CANCELED}
    num_refunded = {(p['item'], p['variation']):
                    (p['cnt'], p['price'], p['price'] - p['tax_value'])
                    for p in counters
                    if p['order__status'] == Order.STATUS_REFUNDED}
    num_paid = {(p['item'], p['variation']):
                (p['cnt'], p['price'], p['price'] - p['tax_value'])
                for p in counters if p['order__status'] == Order.STATUS_PAID}
    num_pending = {(p['item'], p['variation']):
                   (p['cnt'], p['price'], p['price'] - p['tax_value'])
                   for p in counters
                   if p['order__status'] == Order.STATUS_PENDING}
    num_expired = {(p['item'], p['variation']):
                   (p['cnt'], p['price'], p['price'] - p['tax_value'])
                   for p in counters
                   if p['order__status'] == Order.STATUS_EXPIRED}
    num_total = dictsum(num_pending, num_paid)

    for item in items:
        item.all_variations = list(item.variations.all())
        item.has_variations = (len(item.all_variations) > 0)
        if item.has_variations:
            for var in item.all_variations:
                variid = var.id
                var.num_total = num_total.get((item.id, variid), (0, 0, 0))
                var.num_pending = num_pending.get((item.id, variid), (0, 0, 0))
                var.num_expired = num_expired.get((item.id, variid), (0, 0, 0))
                var.num_canceled = num_canceled.get((item.id, variid),
                                                    (0, 0, 0))
                var.num_refunded = num_refunded.get((item.id, variid),
                                                    (0, 0, 0))
                var.num_paid = num_paid.get((item.id, variid), (0, 0, 0))
            item.num_total = tuplesum(var.num_total
                                      for var in item.all_variations)
            item.num_pending = tuplesum(var.num_pending
                                        for var in item.all_variations)
            item.num_expired = tuplesum(var.num_expired
                                        for var in item.all_variations)
            item.num_canceled = tuplesum(var.num_canceled
                                         for var in item.all_variations)
            item.num_refunded = tuplesum(var.num_refunded
                                         for var in item.all_variations)
            item.num_paid = tuplesum(var.num_paid
                                     for var in item.all_variations)
        else:
            item.num_total = num_total.get((item.id, None), (0, 0, 0))
            item.num_pending = num_pending.get((item.id, None), (0, 0, 0))
            item.num_expired = num_expired.get((item.id, None), (0, 0, 0))
            item.num_canceled = num_canceled.get((item.id, None), (0, 0, 0))
            item.num_refunded = num_refunded.get((item.id, None), (0, 0, 0))
            item.num_paid = num_paid.get((item.id, None), (0, 0, 0))

    nonecat = ItemCategory(name=_('Uncategorized'))
    # Regroup those by category
    items_by_category = sorted(
        [
            # a group is a tuple of a category and a list of items
            (cat if cat is not None else nonecat,
             [i for i in items if i.category == cat])
            for cat in set([i.category for i in items])
            # insert categories into a set for uniqueness
            # a set is unsorted, so sort again by category
        ],
        key=lambda group: (group[0].position, group[0].id)
        if (group[0] is not None and group[0].id is not None) else (0, 0))

    for c in items_by_category:
        c[0].num_total = tuplesum(item.num_total for item in c[1])
        c[0].num_pending = tuplesum(item.num_pending for item in c[1])
        c[0].num_expired = tuplesum(item.num_expired for item in c[1])
        c[0].num_canceled = tuplesum(item.num_canceled for item in c[1])
        c[0].num_refunded = tuplesum(item.num_refunded for item in c[1])
        c[0].num_paid = tuplesum(item.num_paid for item in c[1])

    # Payment fees
    payment_cat_obj = DummyObject()
    payment_cat_obj.name = _('Fees')
    payment_items = []

    if not subevent:
        counters = OrderFee.objects.filter(order__event=event).values(
            'fee_type', 'internal_type',
            'order__status').annotate(cnt=Count('id'),
                                      value=Sum('value'),
                                      tax_value=Sum('tax_value')).order_by()

        num_canceled = {(o['fee_type'], o['internal_type']):
                        (o['cnt'], o['value'], o['value'] - o['tax_value'])
                        for o in counters
                        if o['order__status'] == Order.STATUS_CANCELED}
        num_refunded = {(o['fee_type'], o['internal_type']):
                        (o['cnt'], o['value'], o['value'] - o['tax_value'])
                        for o in counters
                        if o['order__status'] == Order.STATUS_REFUNDED}
        num_pending = {(o['fee_type'], o['internal_type']):
                       (o['cnt'], o['value'], o['value'] - o['tax_value'])
                       for o in counters
                       if o['order__status'] == Order.STATUS_PENDING}
        num_expired = {(o['fee_type'], o['internal_type']):
                       (o['cnt'], o['value'], o['value'] - o['tax_value'])
                       for o in counters
                       if o['order__status'] == Order.STATUS_EXPIRED}
        num_paid = {(o['fee_type'], o['internal_type']):
                    (o['cnt'], o['value'], o['value'] - o['tax_value'])
                    for o in counters
                    if o['order__status'] == Order.STATUS_PAID}
        num_total = dictsum(num_pending, num_paid)

        provider_names = {
            k: v.verbose_name
            for k, v in event.get_payment_providers().items()
        }
        names = dict(OrderFee.FEE_TYPES)

        for pprov, total in sorted(num_total.items(), key=lambda i: i[0]):
            ppobj = DummyObject()
            if pprov[0] == OrderFee.FEE_TYPE_PAYMENT:
                ppobj.name = '{} - {}'.format(
                    names[OrderFee.FEE_TYPE_PAYMENT],
                    provider_names.get(pprov[1], pprov[1]))
            else:
                ppobj.name = '{} - {}'.format(names[OrderFee.FEE_TYPE_PAYMENT],
                                              pprov[1])
            ppobj.provider = pprov[1]
            ppobj.has_variations = False
            ppobj.num_total = total
            ppobj.num_canceled = num_canceled.get(pprov, (0, 0, 0))
            ppobj.num_refunded = num_refunded.get(pprov, (0, 0, 0))
            ppobj.num_expired = num_expired.get(pprov, (0, 0, 0))
            ppobj.num_pending = num_pending.get(pprov, (0, 0, 0))
            ppobj.num_paid = num_paid.get(pprov, (0, 0, 0))
            payment_items.append(ppobj)

        payment_cat_obj.num_total = (Dontsum(''),
                                     sum(i.num_total[1]
                                         for i in payment_items),
                                     sum(i.num_total[2]
                                         for i in payment_items))
        payment_cat_obj.num_canceled = (Dontsum(''),
                                        sum(i.num_canceled[1]
                                            for i in payment_items),
                                        sum(i.num_canceled[2]
                                            for i in payment_items))
        payment_cat_obj.num_refunded = (Dontsum(''),
                                        sum(i.num_refunded[1]
                                            for i in payment_items),
                                        sum(i.num_refunded[2]
                                            for i in payment_items))
        payment_cat_obj.num_expired = (Dontsum(''),
                                       sum(i.num_expired[1]
                                           for i in payment_items),
                                       sum(i.num_expired[2]
                                           for i in payment_items))
        payment_cat_obj.num_pending = (Dontsum(''),
                                       sum(i.num_pending[1]
                                           for i in payment_items),
                                       sum(i.num_pending[2]
                                           for i in payment_items))
        payment_cat_obj.num_paid = (Dontsum(''),
                                    sum(i.num_paid[1] for i in payment_items),
                                    sum(i.num_paid[2] for i in payment_items))
        payment_cat = (payment_cat_obj, payment_items)

        items_by_category.append(payment_cat)

    total = {
        'num_total': tuplesum(c.num_total for c, i in items_by_category),
        'num_pending': tuplesum(c.num_pending for c, i in items_by_category),
        'num_expired': tuplesum(c.num_expired for c, i in items_by_category),
        'num_canceled': tuplesum(c.num_canceled for c, i in items_by_category),
        'num_refunded': tuplesum(c.num_refunded for c, i in items_by_category),
        'num_paid': tuplesum(c.num_paid for c, i in items_by_category)
    }

    return items_by_category, total
コード例 #43
0
ファイル: widget.py プロジェクト: regnat/pretix
    def _get_event_list(self, request, **kwargs):
        data = {}
        o = getattr(request, 'event', request.organizer)
        list_type = self.request.GET.get("style", o.settings.event_list_type)
        data['list_type'] = list_type

        if hasattr(self.request,
                   'event') and data['list_type'] not in ("calendar", "week"):
            if self.request.event.subevents.filter(
                    date_from__gt=now()).count() > 50:
                if self.request.event.settings.event_list_type not in (
                        "calendar", "week"):
                    self.request.event.settings.event_list_type = "calendar"
                data['list_type'] = list_type = 'calendar'

        if hasattr(self.request, 'event'):
            data['name'] = str(request.event.name)
            data['frontpage_text'] = str(
                rich_text(request.event.settings.frontpage_text,
                          safelinks=False))

        cache_key = ':'.join([
            'widget.py',
            'eventlist',
            request.organizer.slug,
            request.event.slug if hasattr(request, 'event') else '-',
            list_type,
            request.GET.urlencode(),
            get_language(),
        ])
        cached_data = cache.get(cache_key)
        if cached_data:
            return self.response(cached_data)

        if list_type == "calendar":
            self._set_month_year()
            _, ndays = calendar.monthrange(self.year, self.month)

            data['date'] = date(self.year, self.month, 1)
            if hasattr(self.request, 'event'):
                tz = pytz.timezone(self.request.event.settings.timezone)
            else:
                tz = pytz.UTC
            before = datetime(self.year, self.month, 1, 0, 0, 0,
                              tzinfo=tz) - timedelta(days=1)
            after = datetime(self.year, self.month, ndays, 0, 0, 0,
                             tzinfo=tz) + timedelta(days=1)

            ebd = defaultdict(list)

            if hasattr(self.request, 'event'):
                add_subevents_for_days(
                    filter_qs_by_attr(
                        self.request.event.subevents_annotated('web').filter(
                            event__sales_channels__contains=self.request.
                            sales_channel.identifier), self.request), before,
                    after, ebd, set(), self.request.event,
                    kwargs.get('cart_namespace'))
            else:
                timezones = set()
                add_events_for_days(
                    self.request,
                    filter_qs_by_attr(
                        Event.annotated(
                            self.request.organizer.events,
                            'web').filter(sales_channels__contains=self.
                                          request.sales_channel.identifier),
                        self.request), before, after, ebd, timezones)
                add_subevents_for_days(
                    filter_qs_by_attr(
                        SubEvent.annotated(
                            SubEvent.objects.filter(
                                event__organizer=self.request.organizer,
                                event__is_public=True,
                                event__live=True,
                                event__sales_channels__contains=self.request.
                                sales_channel.identifier).prefetch_related(
                                    'event___settings_objects',
                                    'event__organizer___settings_objects')),
                        self.request), before, after, ebd, timezones)

            data['weeks'] = weeks_for_template(ebd, self.year, self.month)
            for w in data['weeks']:
                for d in w:
                    if not d:
                        continue
                    d['events'] = self._serialize_events(d['events'] or [])
        elif list_type == "week":
            self._set_week_year()

            if hasattr(self.request, 'event'):
                tz = pytz.timezone(self.request.event.settings.timezone)
            else:
                tz = pytz.UTC

            week = isoweek.Week(self.year, self.week)
            data['week'] = [self.year, self.week]
            before = datetime(week.monday().year,
                              week.monday().month,
                              week.monday().day,
                              0,
                              0,
                              0,
                              tzinfo=tz) - timedelta(days=1)
            after = datetime(week.sunday().year,
                             week.sunday().month,
                             week.sunday().day,
                             0,
                             0,
                             0,
                             tzinfo=tz) + timedelta(days=1)

            ebd = defaultdict(list)
            if hasattr(self.request, 'event'):
                add_subevents_for_days(
                    filter_qs_by_attr(
                        self.request.event.subevents_annotated('web'),
                        self.request), before, after, ebd, set(),
                    self.request.event, kwargs.get('cart_namespace'))
            else:
                timezones = set()
                add_events_for_days(
                    self.request,
                    filter_qs_by_attr(
                        Event.annotated(self.request.organizer.events, 'web'),
                        self.request), before, after, ebd, timezones)
                add_subevents_for_days(
                    filter_qs_by_attr(
                        SubEvent.annotated(
                            SubEvent.objects.filter(
                                event__organizer=self.request.organizer,
                                event__is_public=True,
                                event__live=True,
                            ).prefetch_related(
                                'event___settings_objects',
                                'event__organizer___settings_objects')),
                        self.request), before, after, ebd, timezones)

            data['days'] = days_for_template(ebd, week)
            for d in data['days']:
                d['events'] = self._serialize_events(d['events'] or [])
        else:
            if hasattr(self.request, 'event'):
                evs = self.request.event.subevents_sorted(
                    filter_qs_by_attr(
                        self.request.event.subevents_annotated(
                            self.request.sales_channel.identifier),
                        self.request))
                tz = pytz.timezone(request.event.settings.timezone)
                data['events'] = [{
                    'name':
                    str(ev.name),
                    'location':
                    str(ev.location),
                    'date_range':
                    self._get_date_range(ev, ev.event, tz),
                    'availability':
                    self._get_availability(ev, ev.event),
                    'event_url':
                    build_absolute_uri(ev.event, 'presale:event.index'),
                    'subevent':
                    ev.pk,
                } for ev in evs]
            else:
                data['events'] = []
                qs = self._get_event_queryset()
                for event in qs:
                    tz = pytz.timezone(
                        event.cache.get_or_set(
                            'timezone', lambda: event.settings.timezone))
                    if event.has_subevents:
                        dr = daterange(event.min_from.astimezone(tz),
                                       (event.max_fromto or event.max_to
                                        or event.max_from).astimezone(tz))
                        avail = {
                            'color': 'none',
                            'text': gettext('Event series')
                        }
                    else:
                        dr = self._get_date_range(event, event, tz)
                        avail = self._get_availability(event, event)
                    data['events'].append({
                        'name':
                        str(event.name),
                        'location':
                        str(event.location),
                        'date_range':
                        dr,
                        'availability':
                        avail,
                        'event_url':
                        build_absolute_uri(event, 'presale:event.index'),
                    })

        cache.set(cache_key, data, 30)
        # These pages are cached for a really short duration – this should make them pretty accurate, while still
        # providing some protection against burst traffic.
        return self.response(data)
コード例 #44
0
ファイル: stats.py プロジェクト: zhangzhongnan928/pretix
def order_overview(
        event: Event, subevent: SubEvent=None, date_filter='', date_from=None, date_until=None, fees=False,
        admission_only=False
) -> Tuple[List[Tuple[ItemCategory, List[Item]]], Dict[str, Tuple[Decimal, Decimal]]]:
    items = event.items.all().select_related(
        'category',  # for re-grouping
    ).prefetch_related(
        'variations'
    ).order_by('category__position', 'category_id', 'position', 'name')

    qs = OrderPosition.all
    if subevent:
        qs = qs.filter(subevent=subevent)
    if admission_only:
        qs = qs.filter(item__admission=True)
        items = items.filter(admission=True)

    if date_from and isinstance(date_from, date):
        date_from = make_aware(datetime.combine(
            date_from,
            time(hour=0, minute=0, second=0, microsecond=0)
        ), event.timezone)

    if date_until and isinstance(date_until, date):
        date_until = make_aware(datetime.combine(
            date_until + timedelta(days=1),
            time(hour=0, minute=0, second=0, microsecond=0)
        ), event.timezone)

    if date_filter == 'order_date':
        if date_from:
            qs = qs.filter(order__datetime__gte=date_from)
        if date_until:
            qs = qs.filter(order__datetime__lt=date_until)
    elif date_filter == 'last_payment_date':
        p_date = OrderPayment.objects.filter(
            order=OuterRef('order'),
            state__in=[OrderPayment.PAYMENT_STATE_CONFIRMED, OrderPayment.PAYMENT_STATE_REFUNDED],
            payment_date__isnull=False
        ).values('order').annotate(
            m=Max('payment_date')
        ).values('m').order_by()
        qs = qs.annotate(payment_date=Subquery(p_date, output_field=DateTimeField()))
        if date_from:
            qs = qs.filter(payment_date__gte=date_from)
        if date_until:
            qs = qs.filter(payment_date__lt=date_until)

    counters = qs.filter(
        order__event=event
    ).annotate(
        status=Case(
            When(canceled=True, then=Value('c')),
            default=F('order__status')
        )
    ).values(
        'item', 'variation', 'status'
    ).annotate(cnt=Count('id'), price=Sum('price'), tax_value=Sum('tax_value')).order_by()

    states = {
        'canceled': Order.STATUS_CANCELED,
        'paid': Order.STATUS_PAID,
        'pending': Order.STATUS_PENDING,
        'expired': Order.STATUS_EXPIRED,
    }
    num = {}
    for l, s in states.items():
        num[l] = {
            (p['item'], p['variation']): (p['cnt'], p['price'], p['price'] - p['tax_value'])
            for p in counters if p['status'] == s
        }

    num['total'] = dictsum(num['pending'], num['paid'])

    for item in items:
        item.all_variations = list(item.variations.all())
        item.has_variations = (len(item.all_variations) > 0)
        item.num = {}
        if item.has_variations:
            for var in item.all_variations:
                variid = var.id
                var.num = {}
                for l in states.keys():
                    var.num[l] = num[l].get((item.id, variid), (0, 0, 0))
                var.num['total'] = num['total'].get((item.id, variid), (0, 0, 0))
            for l in states.keys():
                item.num[l] = tuplesum(var.num[l] for var in item.all_variations)
            item.num['total'] = tuplesum(var.num['total'] for var in item.all_variations)
        else:
            for l in states.keys():
                item.num[l] = num[l].get((item.id, None), (0, 0, 0))
            item.num['total'] = num['total'].get((item.id, None), (0, 0, 0))

    nonecat = ItemCategory(name=_('Uncategorized'))
    # Regroup those by category
    items_by_category = sorted(
        [
            # a group is a tuple of a category and a list of items
            (cat if cat is not None else nonecat, [i for i in items if i.category == cat])
            for cat in set([i.category for i in items])
            # insert categories into a set for uniqueness
            # a set is unsorted, so sort again by category
        ],
        key=lambda group: (group[0].position, group[0].id) if (
            group[0] is not None and group[0].id is not None) else (0, 0)
    )

    for c in items_by_category:
        c[0].num = {}
        for l in states.keys():
            c[0].num[l] = tuplesum(item.num[l] for item in c[1])
        c[0].num['total'] = tuplesum(item.num['total'] for item in c[1])

    # Payment fees
    payment_cat_obj = DummyObject()
    payment_cat_obj.name = _('Fees')
    payment_items = []

    if not subevent and fees:
        qs = OrderFee.all.filter(
            order__event=event
        ).annotate(
            status=Case(
                When(canceled=True, then=Value('c')),
                default=F('order__status')
            )
        )
        if date_filter == 'order_date':
            if date_from:
                qs = qs.filter(order__datetime__gte=date_from)
            if date_until:
                qs = qs.filter(order__datetime__lt=date_until)
        elif date_filter == 'last_payment_date':
            qs = qs.annotate(payment_date=Subquery(p_date, output_field=DateTimeField()))
            if date_from:
                qs = qs.filter(payment_date__gte=date_from)
            if date_until:
                qs = qs.filter(payment_date__lt=date_until)
        counters = qs.values(
            'fee_type', 'internal_type', 'status'
        ).annotate(cnt=Count('id'), value=Sum('value'), tax_value=Sum('tax_value')).order_by()

        for l, s in states.items():
            num[l] = {
                (o['fee_type'], o['internal_type']): (o['cnt'], o['value'], o['value'] - o['tax_value'])
                for o in counters if o['status'] == s
            }
        num['total'] = dictsum(num['pending'], num['paid'])

        provider_names = {
            k: v.verbose_name
            for k, v in event.get_payment_providers().items()
        }
        names = dict(OrderFee.FEE_TYPES)

        for pprov, total in sorted(num['total'].items(), key=lambda i: i[0]):
            ppobj = DummyObject()
            if pprov[0] == OrderFee.FEE_TYPE_PAYMENT:
                ppobj.name = '{} - {}'.format(names[pprov[0]], provider_names.get(pprov[1], pprov[1]))
            else:
                name = pprov[1]
                for r, resp in order_fee_type_name.send(sender=event, fee_type=pprov[0], internal_type=pprov[1]):
                    if resp:
                        name = resp
                        break

                ppobj.name = '{} - {}'.format(names[pprov[0]], name)
            ppobj.provider = pprov[1]
            ppobj.has_variations = False
            ppobj.num = {}
            for l in states.keys():
                ppobj.num[l] = num[l].get(pprov, (0, 0, 0))
            ppobj.num['total'] = total
            payment_items.append(ppobj)

        payment_cat_obj.num = {}
        for l in states.keys():
            payment_cat_obj.num[l] = (
                Dontsum(''), sum(i.num[l][1] for i in payment_items), sum(i.num[l][2] for i in payment_items)
            )
        payment_cat_obj.num['total'] = (
            Dontsum(''), sum(i.num['total'][1] for i in payment_items), sum(i.num['total'][2] for i in payment_items)
        )
        payment_cat = (payment_cat_obj, payment_items)
        any_payment = any(payment_cat_obj.num[s][1] for s in states.keys())
        if any_payment:
            items_by_category.append(payment_cat)

    total = {
        'num': {'total': tuplesum(c.num['total'] for c, i in items_by_category)}
    }
    for l in states.keys():
        total['num'][l] = tuplesum(c.num[l] for c, i in items_by_category)

    return items_by_category, total
コード例 #45
0
 def sform(self):
     return EventCreateSettingsForm(
         obj=Event(),
         prefix='settings',
         data=self.request.POST if self.request.method == 'POST' else None)