示例#1
0
def _create_order(event: Event,
                  email: str,
                  positions: list,
                  dt: datetime,
                  payment_provider: BasePaymentProvider,
                  locale: str = None):
    total = sum([c.price for c in positions])
    payment_fee = payment_provider.calculate_fee(total)
    total += payment_fee
    expires = [
        dt +
        timedelta(days=event.settings.get('payment_term_days', as_type=int))
    ]
    if event.settings.get('payment_term_last'):
        expires.append(
            event.settings.get('payment_term_last', as_type=datetime))
    order = Order.objects.create(status=Order.STATUS_PENDING,
                                 event=event,
                                 email=email,
                                 datetime=dt,
                                 expires=min(expires),
                                 locale=locale,
                                 total=total,
                                 payment_fee=payment_fee,
                                 payment_provider=payment_provider.identifier)
    OrderPosition.transform_cart_positions(positions, order)
    order_placed.send(event, order=order)
    return order
示例#2
0
文件: orders.py 项目: rahmonov/pretix
def _create_order(event: Event, email: str, positions: List[CartPosition], now_dt: datetime,
                  payment_provider: BasePaymentProvider, locale: str=None, address: int=None,
                  meta_info: dict=None):
    from datetime import date, time

    total = sum([c.price for c in positions])
    payment_fee = payment_provider.calculate_fee(total)
    total += payment_fee

    tz = pytz.timezone(event.settings.timezone)
    exp_by_date = now_dt.astimezone(tz) + timedelta(days=event.settings.get('payment_term_days', as_type=int))
    exp_by_date = exp_by_date.astimezone(tz).replace(hour=23, minute=59, second=59, microsecond=0)
    if event.settings.get('payment_term_weekdays'):
        if exp_by_date.weekday() == 5:
            exp_by_date += timedelta(days=2)
        elif exp_by_date.weekday() == 6:
            exp_by_date += timedelta(days=1)

    expires = exp_by_date

    if event.settings.get('payment_term_last'):
        last_date = make_aware(datetime.combine(
            event.settings.get('payment_term_last', as_type=date),
            time(hour=23, minute=59, second=59)
        ), tz)
        if last_date < expires:
            expires = last_date

    with transaction.atomic():
        order = Order.objects.create(
            status=Order.STATUS_PENDING,
            event=event,
            email=email,
            datetime=now_dt,
            expires=expires,
            locale=locale,
            total=total,
            payment_fee=payment_fee,
            payment_provider=payment_provider.identifier,
            meta_info=json.dumps(meta_info or {}),
        )
        OrderPosition.transform_cart_positions(positions, order)

        if address is not None:
            try:
                addr = InvoiceAddress.objects.get(
                    pk=address
                )
                if addr.order is not None:
                    addr.pk = None
                addr.order = order
                addr.save()
            except InvoiceAddress.DoesNotExist:
                pass

        order.log_action('pretix.event.order.placed')

    order_placed.send(event, order=order)
    return order
示例#3
0
文件: orders.py 项目: rixx/pretix
def _create_order(event: Event, email: str, positions: List[CartPosition], now_dt: datetime,
                  payment_provider: BasePaymentProvider, locale: str=None, address: int=None,
                  meta_info: dict=None):
    from datetime import date, time

    total = sum([c.price for c in positions])
    payment_fee = payment_provider.calculate_fee(total)
    total += payment_fee

    tz = pytz.timezone(event.settings.timezone)
    exp_by_date = now_dt.astimezone(tz) + timedelta(days=event.settings.get('payment_term_days', as_type=int))
    exp_by_date = exp_by_date.replace(hour=23, minute=59, second=59, microsecond=0)
    if event.settings.get('payment_term_weekdays'):
        if exp_by_date.weekday() == 5:
            exp_by_date += timedelta(days=2)
        elif exp_by_date.weekday() == 6:
            exp_by_date += timedelta(days=1)

    expires = exp_by_date

    if event.settings.get('payment_term_last'):
        last_date = make_aware(datetime.combine(
            event.settings.get('payment_term_last', as_type=date),
            time(hour=23, minute=59, second=59)
        ), tz)
        if last_date < expires:
            expires = last_date

    order = Order.objects.create(
        status=Order.STATUS_PENDING,
        event=event,
        email=email,
        datetime=now_dt,
        expires=expires,
        locale=locale,
        total=total,
        payment_fee=payment_fee,
        payment_provider=payment_provider.identifier,
        meta_info=json.dumps(meta_info or {}),
    )
    OrderPosition.transform_cart_positions(positions, order)

    if address is not None:
        try:
            addr = InvoiceAddress.objects.get(
                pk=address
            )
            if addr.order is not None:
                addr.pk = None
            addr.order = order
            addr.save()
        except InvoiceAddress.DoesNotExist:
            pass

    order.log_action('pretix.event.order.placed')
    order_placed.send(event, order=order)
    return order
示例#4
0
    def _get_object(self, op: OrderPosition):
        meta_info = json.loads(op.meta_info or '{}')

        ticket_object = self._generate_object(op)

        if ticket_object and 'googlepaypass' not in meta_info:
            meta_info['googlepaypass'] = ticket_object['id']
            op.meta_info = json.dumps(meta_info)
            op.save(update_fields=['meta_info'])
            return ticket_object
        elif ticket_object and 'googlepaypass' in meta_info:
            return ticket_object
        else:
            return False
示例#5
0
    def create(self, validated_data):
        fees_data = validated_data.pop('fees') if 'fees' in validated_data else []
        positions_data = validated_data.pop('positions') if 'positions' in validated_data else []
        if 'invoice_address' in validated_data:
            ia = InvoiceAddress(**validated_data.pop('invoice_address'))
        else:
            ia = None

        with self.context['event'].lock():
            quotadiff = Counter()
            for pos_data in positions_data:
                new_quotas = (pos_data.get('variation').quotas.filter(subevent=pos_data.get('subevent'))
                              if pos_data.get('variation')
                              else pos_data.get('item').quotas.filter(subevent=pos_data.get('subevent')))
                quotadiff.update(new_quotas)

                for quota, diff in quotadiff.items():
                    avail = quota.availability()
                    if avail[0] != Quota.AVAILABILITY_OK or (avail[1] is not None and avail[1] < diff):
                        raise ValidationError(
                            'There is not enough quota available on quota "{}" to perform the operation.'.format(
                                quota.name
                            )
                        )

            order = Order(event=self.context['event'], **validated_data)
            order.set_expires(subevents=[p['subevent'] for p in positions_data])
            order.total = sum([p['price'] for p in positions_data]) + sum([f['value'] for f in fees_data], Decimal('0.00'))
            if order.total == Decimal('0.00') and validated_data.get('status') != Order.STATUS_PAID:
                order.payment_provider = 'free'
                order.status = Order.STATUS_PAID
            elif order.payment_provider == "free" and order.total != Decimal('0.00'):
                raise ValidationError('You cannot use the "free" payment provider for non-free orders.')
            order.save()
            if ia:
                ia.order = order
                ia.save()
            pos_map = {}
            for pos_data in positions_data:
                answers_data = pos_data.pop('answers')
                addon_to = pos_data.pop('addon_to')
                pos = OrderPosition(**pos_data)
                pos.order = order
                pos._calculate_tax()
                if addon_to:
                    pos.addon_to = pos_map[addon_to]
                pos.save()
                pos_map[pos.positionid] = pos
                for answ_data in answers_data:
                    options = answ_data.pop('options')
                    answ = pos.answers.create(**answ_data)
                    answ.options.add(*options)
        for fee_data in fees_data:
            f = OrderFee(**fee_data)
            f.order = order
            f._calculate_tax()
            f.save()

        return order
示例#6
0
def _create_order(event: Event,
                  email: str,
                  positions: List[CartPosition],
                  now_dt: datetime,
                  payment_provider: BasePaymentProvider,
                  locale: str = None,
                  address: int = None):
    total = sum([c.price for c in positions])
    payment_fee = payment_provider.calculate_fee(total)
    total += payment_fee

    exp_by_date = now_dt + timedelta(
        days=event.settings.get('payment_term_days', as_type=int))
    if event.settings.get('payment_term_weekdays'):
        if exp_by_date.weekday() == 5:
            exp_by_date += timedelta(days=2)
        elif exp_by_date.weekday() == 6:
            exp_by_date += timedelta(days=1)

    expires = [exp_by_date]
    if event.settings.get('payment_term_last'):
        expires.append(
            event.settings.get('payment_term_last', as_type=datetime))
    order = Order.objects.create(status=Order.STATUS_PENDING,
                                 event=event,
                                 email=email,
                                 datetime=now_dt,
                                 expires=min(expires),
                                 locale=locale,
                                 total=total,
                                 payment_fee=payment_fee,
                                 payment_provider=payment_provider.identifier)
    OrderPosition.transform_cart_positions(positions, order)

    if address is not None:
        try:
            addr = InvoiceAddress.objects.get(pk=address)
            if addr.order is not None:
                addr.pk = None
            addr.order = order
            addr.save()
        except InvoiceAddress.DoesNotExist:
            pass

    order.log_action('pretix.event.order.placed')
    order_placed.send(event, order=order)
    return order
示例#7
0
文件: orders.py 项目: cherti/pretix
def _create_order(event: Event, email: str, positions: List[CartPosition], now_dt: datetime,
                  payment_provider: BasePaymentProvider, locale: str=None, address: int=None):
    total = sum([c.price for c in positions])
    payment_fee = payment_provider.calculate_fee(total)
    total += payment_fee

    exp_by_date = now_dt + timedelta(days=event.settings.get('payment_term_days', as_type=int))
    if event.settings.get('payment_term_weekdays'):
        if exp_by_date.weekday() == 5:
            exp_by_date += timedelta(days=2)
        elif exp_by_date.weekday() == 6:
            exp_by_date += timedelta(days=1)

    expires = [exp_by_date]
    if event.settings.get('payment_term_last'):
        expires.append(event.settings.get('payment_term_last', as_type=datetime))
    order = Order.objects.create(
        status=Order.STATUS_PENDING,
        event=event,
        email=email,
        datetime=now_dt,
        expires=min(expires),
        locale=locale,
        total=total,
        payment_fee=payment_fee,
        payment_provider=payment_provider.identifier
    )
    OrderPosition.transform_cart_positions(positions, order)

    if address is not None:
        try:
            addr = InvoiceAddress.objects.get(
                pk=address
            )
            if addr.order is not None:
                addr.pk = None
            addr.order = order
            addr.save()
        except InvoiceAddress.DoesNotExist:
            pass

    order.log_action('pretix.event.order.placed')
    order_placed.send(event, order=order)
    return order
示例#8
0
def place_order(event, user, positions, dt, payment_provider):
    total = sum([c.price for c in positions])
    payment_fee = payment_provider.calculate_fee(total)
    total += payment_fee
    expires = [dt + timedelta(days=event.settings.get('payment_term_days', as_type=int))]
    if event.settings.get('payment_term_last'):
        expires.append(event.settings.get('payment_term_last', as_type=datetime))
    order = Order.objects.create(
        status=Order.STATUS_PENDING,
        event=event,
        user=user,
        datetime=dt,
        expires=min(expires),
        total=total,
        payment_fee=payment_fee,
        payment_provider=payment_provider.identifier,
    )
    OrderPosition.transform_cart_positions(positions, order)
    return order
示例#9
0
文件: orders.py 项目: cygery/pretix
def _create_order(event: Event, email: str, positions: list, dt: datetime,
                  payment_provider: BasePaymentProvider, locale: str=None):
    total = sum([c.price for c in positions])
    payment_fee = payment_provider.calculate_fee(total)
    total += payment_fee
    expires = [dt + timedelta(days=event.settings.get('payment_term_days', as_type=int))]
    if event.settings.get('payment_term_last'):
        expires.append(event.settings.get('payment_term_last', as_type=datetime))
    order = Order.objects.create(
        status=Order.STATUS_PENDING,
        event=event,
        email=email,
        datetime=dt,
        expires=min(expires),
        locale=locale,
        total=total,
        payment_fee=payment_fee,
        payment_provider=payment_provider.identifier
    )
    OrderPosition.transform_cart_positions(positions, order)
    order_placed.send(event, order=order)
    return order
示例#10
0
def _create_order(event: Event, email: str, positions: List[CartPosition], now_dt: datetime,
                  payment_provider: BasePaymentProvider, locale: str=None, address: int=None,
                  meta_info: dict=None):
    from datetime import time

    total = sum([c.price for c in positions])
    payment_fee = payment_provider.calculate_fee(total)
    total += payment_fee

    tz = pytz.timezone(event.settings.timezone)
    exp_by_date = now_dt.astimezone(tz) + timedelta(days=event.settings.get('payment_term_days', as_type=int))
    exp_by_date = exp_by_date.astimezone(tz).replace(hour=23, minute=59, second=59, microsecond=0)
    if event.settings.get('payment_term_weekdays'):
        if exp_by_date.weekday() == 5:
            exp_by_date += timedelta(days=2)
        elif exp_by_date.weekday() == 6:
            exp_by_date += timedelta(days=1)

    expires = exp_by_date

    term_last = event.settings.get('payment_term_last', as_type=RelativeDateWrapper)
    if term_last:
        if event.has_subevents:
            term_last = min([
                term_last.datetime(se).date()
                for se in event.subevents.filter(id__in=[p.subevent_id for p in positions])
            ])
        else:
            term_last = term_last.datetime(event).date()
        term_last = make_aware(datetime.combine(
            term_last,
            time(hour=23, minute=59, second=59)
        ), tz)
        if term_last < expires:
            expires = term_last

    with transaction.atomic():
        order = Order.objects.create(
            status=Order.STATUS_PENDING,
            event=event,
            email=email,
            datetime=now_dt,
            expires=expires,
            locale=locale,
            total=total,
            payment_fee=payment_fee,
            payment_provider=payment_provider.identifier,
            meta_info=json.dumps(meta_info or {}),
        )

        if address:
            if address.order is not None:
                address.pk = None
            address.order = order
            address.save()

            order._calculate_tax()  # Might have changed due to new invoice address
            order.save()

        OrderPosition.transform_cart_positions(positions, order)
        order.log_action('pretix.event.order.placed')

    order_placed.send(event, order=order)
    return order
示例#11
0
文件: order.py 项目: ryardley/pretix
    def create(self, validated_data):
        fees_data = validated_data.pop(
            'fees') if 'fees' in validated_data else []
        positions_data = validated_data.pop(
            'positions') if 'positions' in validated_data else []
        payment_provider = validated_data.pop('payment_provider')
        payment_info = validated_data.pop('payment_info', '{}')

        if 'invoice_address' in validated_data:
            iadata = validated_data.pop('invoice_address')
            name = iadata.pop('name', '')
            if name and not iadata.get('name_parts'):
                iadata['name_parts'] = {'_legacy': name}
            ia = InvoiceAddress(**iadata)
        else:
            ia = None

        with self.context['event'].lock() as now_dt:
            quotadiff = Counter()

            consume_carts = validated_data.pop('consume_carts', [])
            delete_cps = []
            quota_avail_cache = {}
            if consume_carts:
                for cp in CartPosition.objects.filter(
                        event=self.context['event'],
                        cart_id__in=consume_carts):
                    quotas = (cp.variation.quotas.filter(subevent=cp.subevent)
                              if cp.variation else cp.item.quotas.filter(
                                  subevent=cp.subevent))
                    for quota in quotas:
                        if quota not in quota_avail_cache:
                            quota_avail_cache[quota] = list(
                                quota.availability())
                        if quota_avail_cache[quota][1] is not None:
                            quota_avail_cache[quota][1] += 1
                    if cp.expires > now_dt:
                        quotadiff.subtract(quotas)
                    delete_cps.append(cp)

            errs = [{} for p in positions_data]

            for i, pos_data in enumerate(positions_data):
                new_quotas = (pos_data.get('variation').quotas.filter(
                    subevent=pos_data.get('subevent'))
                              if pos_data.get('variation') else
                              pos_data.get('item').quotas.filter(
                                  subevent=pos_data.get('subevent')))
                if len(new_quotas) == 0:
                    errs[i]['item'] = [
                        ugettext_lazy(
                            'The product "{}" is not assigned to a quota.').
                        format(str(pos_data.get('item')))
                    ]
                else:
                    for quota in new_quotas:
                        if quota not in quota_avail_cache:
                            quota_avail_cache[quota] = list(
                                quota.availability())

                        if quota_avail_cache[quota][1] is not None:
                            quota_avail_cache[quota][1] -= 1
                            if quota_avail_cache[quota][1] < 0:
                                errs[i]['item'] = [
                                    ugettext_lazy(
                                        'There is not enough quota available on quota "{}" to perform the operation.'
                                    ).format(quota.name)
                                ]

                quotadiff.update(new_quotas)

            if any(errs):
                raise ValidationError({'positions': errs})

            if validated_data.get('locale', None) is None:
                validated_data['locale'] = self.context[
                    'event'].settings.locale
            order = Order(event=self.context['event'], **validated_data)
            order.set_expires(
                subevents=[p.get('subevent') for p in positions_data])
            order.total = sum([p['price'] for p in positions_data]) + sum(
                [f['value'] for f in fees_data], Decimal('0.00'))
            order.meta_info = "{}"
            order.save()

            if order.total == Decimal('0.00') and validated_data.get(
                    'status') != Order.STATUS_PAID:
                order.status = Order.STATUS_PAID
                order.save()
                order.payments.create(
                    amount=order.total,
                    provider='free',
                    state=OrderPayment.PAYMENT_STATE_CONFIRMED,
                    payment_date=now())
            elif payment_provider == "free" and order.total != Decimal('0.00'):
                raise ValidationError(
                    'You cannot use the "free" payment provider for non-free orders.'
                )
            elif validated_data.get('status') == Order.STATUS_PAID:
                order.payments.create(
                    amount=order.total,
                    provider=payment_provider,
                    info=payment_info,
                    payment_date=now(),
                    state=OrderPayment.PAYMENT_STATE_CONFIRMED)
            elif payment_provider:
                order.payments.create(amount=order.total,
                                      provider=payment_provider,
                                      info=payment_info,
                                      state=OrderPayment.PAYMENT_STATE_CREATED)

            if ia:
                ia.order = order
                ia.save()
            pos_map = {}
            for pos_data in positions_data:
                answers_data = pos_data.pop('answers', [])
                addon_to = pos_data.pop('addon_to', None)
                attendee_name = pos_data.pop('attendee_name', '')
                if attendee_name and not pos_data.get('attendee_name_parts'):
                    pos_data['attendee_name_parts'] = {
                        '_legacy': attendee_name
                    }
                pos = OrderPosition(**pos_data)
                pos.order = order
                pos._calculate_tax()
                if addon_to:
                    pos.addon_to = pos_map[addon_to]
                pos.save()
                pos_map[pos.positionid] = pos
                for answ_data in answers_data:
                    options = answ_data.pop('options', [])
                    answ = pos.answers.create(**answ_data)
                    answ.options.add(*options)

            for cp in delete_cps:
                cp.delete()
        for fee_data in fees_data:
            f = OrderFee(**fee_data)
            f.order = order
            f._calculate_tax()
            f.save()

        return order
示例#12
0
    def create(self, validated_data):
        fees_data = validated_data.pop(
            'fees') if 'fees' in validated_data else []
        positions_data = validated_data.pop(
            'positions') if 'positions' in validated_data else []
        payment_provider = validated_data.pop('payment_provider', None)
        payment_info = validated_data.pop('payment_info', '{}')
        payment_date = validated_data.pop('payment_date', now())
        force = validated_data.pop('force', False)
        self._send_mail = validated_data.pop('send_mail', False)

        if 'invoice_address' in validated_data:
            iadata = validated_data.pop('invoice_address')
            name = iadata.pop('name', '')
            if name and not iadata.get('name_parts'):
                iadata['name_parts'] = {'_legacy': name}
            ia = InvoiceAddress(**iadata)
        else:
            ia = None

        with self.context['event'].lock() as now_dt:
            free_seats = set()
            seats_seen = set()
            consume_carts = validated_data.pop('consume_carts', [])
            delete_cps = []
            quota_avail_cache = {}
            v_budget = {}
            voucher_usage = Counter()
            if consume_carts:
                for cp in CartPosition.objects.filter(
                        event=self.context['event'],
                        cart_id__in=consume_carts,
                        expires__gt=now()):
                    quotas = (cp.variation.quotas.filter(subevent=cp.subevent)
                              if cp.variation else cp.item.quotas.filter(
                                  subevent=cp.subevent))
                    for quota in quotas:
                        if quota not in quota_avail_cache:
                            quota_avail_cache[quota] = list(
                                quota.availability())
                        if quota_avail_cache[quota][1] is not None:
                            quota_avail_cache[quota][1] += 1
                    if cp.voucher:
                        voucher_usage[cp.voucher] -= 1
                    if cp.expires > now_dt:
                        if cp.seat:
                            free_seats.add(cp.seat)
                    delete_cps.append(cp)

            errs = [{} for p in positions_data]

            for i, pos_data in enumerate(positions_data):

                if pos_data.get('voucher'):
                    v = pos_data['voucher']

                    if pos_data.get('addon_to'):
                        errs[i]['voucher'] = [
                            'Vouchers are currently not supported for add-on products.'
                        ]
                        continue

                    if not v.applies_to(pos_data['item'],
                                        pos_data.get('variation')):
                        errs[i]['voucher'] = [
                            error_messages['voucher_invalid_item']
                        ]
                        continue

                    if v.subevent_id and pos_data.get(
                            'subevent').pk != v.subevent_id:
                        errs[i]['voucher'] = [
                            error_messages['voucher_invalid_subevent']
                        ]
                        continue

                    if v.valid_until is not None and v.valid_until < now_dt:
                        errs[i]['voucher'] = [
                            error_messages['voucher_expired']
                        ]
                        continue

                    voucher_usage[v] += 1
                    if voucher_usage[v] > 0:
                        redeemed_in_carts = CartPosition.objects.filter(
                            Q(voucher=pos_data['voucher'])
                            & Q(event=self.context['event'])
                            & Q(expires__gte=now_dt)).exclude(
                                pk__in=[cp.pk for cp in delete_cps])
                        v_avail = v.max_usages - v.redeemed - redeemed_in_carts.count(
                        )
                        if v_avail < voucher_usage[v]:
                            errs[i]['voucher'] = [
                                'The voucher has already been used the maximum number of times.'
                            ]

                    if v.budget is not None:
                        price = pos_data.get('price')
                        if price is None:
                            price = get_price(
                                item=pos_data.get('item'),
                                variation=pos_data.get('variation'),
                                voucher=v,
                                custom_price=None,
                                subevent=pos_data.get('subevent'),
                                addon_to=pos_data.get('addon_to'),
                                invoice_address=ia,
                            ).gross
                        pbv = get_price(
                            item=pos_data['item'],
                            variation=pos_data.get('variation'),
                            voucher=None,
                            custom_price=None,
                            subevent=pos_data.get('subevent'),
                            addon_to=pos_data.get('addon_to'),
                            invoice_address=ia,
                        )

                        if v not in v_budget:
                            v_budget[v] = v.budget - v.budget_used()
                        disc = pbv.gross - price
                        if disc > v_budget[v]:
                            new_disc = v_budget[v]
                            v_budget[v] -= new_disc
                            if new_disc == Decimal('0.00') or pos_data.get(
                                    'price') is not None:
                                errs[i]['voucher'] = [
                                    'The voucher has a remaining budget of {}, therefore a discount of {} can not be '
                                    'given.'.format(v_budget[v] + new_disc,
                                                    disc)
                                ]
                                continue
                            pos_data['price'] = price + (disc - new_disc)
                        else:
                            v_budget[v] -= disc

                seated = pos_data.get('item').seat_category_mappings.filter(
                    subevent=pos_data.get('subevent')).exists()
                if pos_data.get('seat'):
                    if not seated:
                        errs[i]['seat'] = [
                            'The specified product does not allow to choose a seat.'
                        ]
                    try:
                        seat = self.context['event'].seats.get(
                            seat_guid=pos_data['seat'],
                            subevent=pos_data.get('subevent'))
                    except Seat.DoesNotExist:
                        errs[i]['seat'] = [
                            'The specified seat does not exist.'
                        ]
                    else:
                        pos_data['seat'] = seat
                        if (seat not in free_seats and not seat.is_available(
                                sales_channel=validated_data.get(
                                    'sales_channel', 'web'))
                            ) or seat in seats_seen:
                            errs[i]['seat'] = [
                                ugettext_lazy(
                                    'The selected seat "{seat}" is not available.'
                                ).format(seat=seat.name)
                            ]
                        seats_seen.add(seat)
                elif seated:
                    errs[i]['seat'] = [
                        'The specified product requires to choose a seat.'
                    ]

            if not force:
                for i, pos_data in enumerate(positions_data):
                    if pos_data.get('voucher'):
                        if pos_data['voucher'].allow_ignore_quota or pos_data[
                                'voucher'].block_quota:
                            continue

                    new_quotas = (pos_data.get('variation').quotas.filter(
                        subevent=pos_data.get('subevent'))
                                  if pos_data.get('variation') else
                                  pos_data.get('item').quotas.filter(
                                      subevent=pos_data.get('subevent')))
                    if len(new_quotas) == 0:
                        errs[i]['item'] = [
                            ugettext_lazy(
                                'The product "{}" is not assigned to a quota.'
                            ).format(str(pos_data.get('item')))
                        ]
                    else:
                        for quota in new_quotas:
                            if quota not in quota_avail_cache:
                                quota_avail_cache[quota] = list(
                                    quota.availability())

                            if quota_avail_cache[quota][1] is not None:
                                quota_avail_cache[quota][1] -= 1
                                if quota_avail_cache[quota][1] < 0:
                                    errs[i]['item'] = [
                                        ugettext_lazy(
                                            'There is not enough quota available on quota "{}" to perform the operation.'
                                        ).format(quota.name)
                                    ]

            if any(errs):
                raise ValidationError({'positions': errs})

            if validated_data.get('locale', None) is None:
                validated_data['locale'] = self.context[
                    'event'].settings.locale
            order = Order(event=self.context['event'], **validated_data)
            order.set_expires(
                subevents=[p.get('subevent') for p in positions_data])
            order.meta_info = "{}"
            order.total = Decimal('0.00')
            order.save()

            if ia:
                ia.order = order
                ia.save()

            pos_map = {}
            for pos_data in positions_data:
                answers_data = pos_data.pop('answers', [])
                addon_to = pos_data.pop('addon_to', None)
                attendee_name = pos_data.pop('attendee_name', '')
                if attendee_name and not pos_data.get('attendee_name_parts'):
                    pos_data['attendee_name_parts'] = {
                        '_legacy': attendee_name
                    }
                pos = OrderPosition(**pos_data)
                pos.order = order
                if addon_to:
                    pos.addon_to = pos_map[addon_to]

                if pos.price is None:
                    price = get_price(
                        item=pos.item,
                        variation=pos.variation,
                        voucher=pos.voucher,
                        custom_price=None,
                        subevent=pos.subevent,
                        addon_to=pos.addon_to,
                        invoice_address=ia,
                    )
                    pos.price = price.gross
                    pos.tax_rate = price.rate
                    pos.tax_value = price.tax
                    pos.tax_rule = pos.item.tax_rule
                else:
                    pos._calculate_tax()

                pos.price_before_voucher = get_price(
                    item=pos.item,
                    variation=pos.variation,
                    voucher=None,
                    custom_price=None,
                    subevent=pos.subevent,
                    addon_to=pos.addon_to,
                    invoice_address=ia,
                ).gross

                if pos.voucher:
                    Voucher.objects.filter(pk=pos.voucher.pk).update(
                        redeemed=F('redeemed') + 1)
                pos.save()
                pos_map[pos.positionid] = pos
                for answ_data in answers_data:
                    options = answ_data.pop('options', [])
                    answ = pos.answers.create(**answ_data)
                    answ.options.add(*options)

            for cp in delete_cps:
                cp.delete()

        order.total = sum([p.price for p in order.positions.all()])
        for fee_data in fees_data:
            is_percentage = fee_data.pop('_treat_value_as_percentage', False)
            if is_percentage:
                fee_data['value'] = round_decimal(
                    order.total * (fee_data['value'] / Decimal('100.00')),
                    self.context['event'].currency)
            is_split_taxes = fee_data.pop('_split_taxes_like_products', False)

            if is_split_taxes:
                d = defaultdict(lambda: Decimal('0.00'))
                trz = TaxRule.zero()
                for p in pos_map.values():
                    tr = p.tax_rule
                    d[tr] += p.price - p.tax_value

                base_values = sorted([tuple(t) for t in d.items()],
                                     key=lambda t: (t[0] or trz).rate)
                sum_base = sum(t[1] for t in base_values)
                fee_values = [
                    (t[0],
                     round_decimal(fee_data['value'] * t[1] / sum_base,
                                   self.context['event'].currency))
                    for t in base_values
                ]
                sum_fee = sum(t[1] for t in fee_values)

                # If there are rounding differences, we fix them up, but always leaning to the benefit of the tax
                # authorities
                if sum_fee > fee_data['value']:
                    fee_values[0] = (fee_values[0][0], fee_values[0][1] +
                                     (fee_data['value'] - sum_fee))
                elif sum_fee < fee_data['value']:
                    fee_values[-1] = (fee_values[-1][0], fee_values[-1][1] +
                                      (fee_data['value'] - sum_fee))

                for tr, val in fee_values:
                    fee_data['tax_rule'] = tr
                    fee_data['value'] = val
                    f = OrderFee(**fee_data)
                    f.order = order
                    f._calculate_tax()
                    f.save()
            else:
                f = OrderFee(**fee_data)
                f.order = order
                f._calculate_tax()
                f.save()

        order.total += sum([f.value for f in order.fees.all()])
        order.save(update_fields=['total'])

        if order.total == Decimal('0.00') and validated_data.get(
                'status') == Order.STATUS_PAID and not payment_provider:
            payment_provider = 'free'

        if order.total == Decimal(
                '0.00') and validated_data.get('status') != Order.STATUS_PAID:
            order.status = Order.STATUS_PAID
            order.save()
            order.payments.create(amount=order.total,
                                  provider='free',
                                  state=OrderPayment.PAYMENT_STATE_CONFIRMED,
                                  payment_date=now())
        elif payment_provider == "free" and order.total != Decimal('0.00'):
            raise ValidationError(
                'You cannot use the "free" payment provider for non-free orders.'
            )
        elif validated_data.get('status') == Order.STATUS_PAID:
            if not payment_provider:
                raise ValidationError(
                    'You cannot create a paid order without a payment provider.'
                )
            order.payments.create(amount=order.total,
                                  provider=payment_provider,
                                  info=payment_info,
                                  payment_date=payment_date,
                                  state=OrderPayment.PAYMENT_STATE_CONFIRMED)
        elif payment_provider:
            order.payments.create(amount=order.total,
                                  provider=payment_provider,
                                  info=payment_info,
                                  state=OrderPayment.PAYMENT_STATE_CREATED)

        return order
示例#13
0
文件: order.py 项目: nkhanal0/pretix
    def create(self, validated_data):
        fees_data = validated_data.pop(
            'fees') if 'fees' in validated_data else []
        positions_data = validated_data.pop(
            'positions') if 'positions' in validated_data else []
        payment_provider = validated_data.pop('payment_provider', None)
        payment_info = validated_data.pop('payment_info', '{}')
        payment_date = validated_data.pop('payment_date', now())
        force = validated_data.pop('force', False)
        self._send_mail = validated_data.pop('send_mail', False)

        if 'invoice_address' in validated_data:
            iadata = validated_data.pop('invoice_address')
            name = iadata.pop('name', '')
            if name and not iadata.get('name_parts'):
                iadata['name_parts'] = {'_legacy': name}
            ia = InvoiceAddress(**iadata)
        else:
            ia = None

        with self.context['event'].lock() as now_dt:
            free_seats = set()
            seats_seen = set()
            consume_carts = validated_data.pop('consume_carts', [])
            delete_cps = []
            quota_avail_cache = {}
            voucher_usage = Counter()
            if consume_carts:
                for cp in CartPosition.objects.filter(
                        event=self.context['event'],
                        cart_id__in=consume_carts,
                        expires__gt=now()):
                    quotas = (cp.variation.quotas.filter(subevent=cp.subevent)
                              if cp.variation else cp.item.quotas.filter(
                                  subevent=cp.subevent))
                    for quota in quotas:
                        if quota not in quota_avail_cache:
                            quota_avail_cache[quota] = list(
                                quota.availability())
                        if quota_avail_cache[quota][1] is not None:
                            quota_avail_cache[quota][1] += 1
                    if cp.voucher:
                        voucher_usage[cp.voucher] -= 1
                    if cp.expires > now_dt:
                        if cp.seat:
                            free_seats.add(cp.seat)
                    delete_cps.append(cp)

            errs = [{} for p in positions_data]

            for i, pos_data in enumerate(positions_data):
                if pos_data.get('voucher'):
                    v = pos_data['voucher']

                    if not v.applies_to(pos_data['item'],
                                        pos_data.get('variation')):
                        errs[i]['voucher'] = [
                            error_messages['voucher_invalid_item']
                        ]
                        continue

                    if v.subevent_id and pos_data.get(
                            'subevent').pk != v.subevent_id:
                        errs[i]['voucher'] = [
                            error_messages['voucher_invalid_subevent']
                        ]
                        continue

                    if v.valid_until is not None and v.valid_until < now_dt:
                        errs[i]['voucher'] = [
                            error_messages['voucher_expired']
                        ]
                        continue

                    voucher_usage[v] += 1
                    if voucher_usage[v] > 0:
                        redeemed_in_carts = CartPosition.objects.filter(
                            Q(voucher=pos_data['voucher'])
                            & Q(event=self.context['event'])
                            & Q(expires__gte=now_dt)).exclude(
                                pk__in=[cp.pk for cp in delete_cps])
                        v_avail = v.max_usages - v.redeemed - redeemed_in_carts.count(
                        )
                        if v_avail < voucher_usage[v]:
                            errs[i]['voucher'] = [
                                'The voucher has already been used the maximum number of times.'
                            ]

                seated = pos_data.get('item').seat_category_mappings.filter(
                    subevent=pos_data.get('subevent')).exists()
                if pos_data.get('seat'):
                    if not seated:
                        errs[i]['seat'] = [
                            'The specified product does not allow to choose a seat.'
                        ]
                    try:
                        seat = self.context['event'].seats.get(
                            seat_guid=pos_data['seat'],
                            subevent=pos_data.get('subevent'))
                    except Seat.DoesNotExist:
                        errs[i]['seat'] = [
                            'The specified seat does not exist.'
                        ]
                    else:
                        pos_data['seat'] = seat
                        if (seat not in free_seats and
                                not seat.is_available()) or seat in seats_seen:
                            errs[i]['seat'] = [
                                ugettext_lazy(
                                    'The selected seat "{seat}" is not available.'
                                ).format(seat=seat.name)
                            ]
                        seats_seen.add(seat)
                elif seated:
                    errs[i]['seat'] = [
                        'The specified product requires to choose a seat.'
                    ]

            if not force:
                for i, pos_data in enumerate(positions_data):
                    if pos_data.get('voucher'):
                        if pos_data['voucher'].allow_ignore_quota or pos_data[
                                'voucher'].block_quota:
                            continue

                    new_quotas = (pos_data.get('variation').quotas.filter(
                        subevent=pos_data.get('subevent'))
                                  if pos_data.get('variation') else
                                  pos_data.get('item').quotas.filter(
                                      subevent=pos_data.get('subevent')))
                    if len(new_quotas) == 0:
                        errs[i]['item'] = [
                            ugettext_lazy(
                                'The product "{}" is not assigned to a quota.'
                            ).format(str(pos_data.get('item')))
                        ]
                    else:
                        for quota in new_quotas:
                            if quota not in quota_avail_cache:
                                quota_avail_cache[quota] = list(
                                    quota.availability())

                            if quota_avail_cache[quota][1] is not None:
                                quota_avail_cache[quota][1] -= 1
                                if quota_avail_cache[quota][1] < 0:
                                    errs[i]['item'] = [
                                        ugettext_lazy(
                                            'There is not enough quota available on quota "{}" to perform the operation.'
                                        ).format(quota.name)
                                    ]

            if any(errs):
                raise ValidationError({'positions': errs})

            if validated_data.get('locale', None) is None:
                validated_data['locale'] = self.context[
                    'event'].settings.locale
            order = Order(event=self.context['event'], **validated_data)
            order.set_expires(
                subevents=[p.get('subevent') for p in positions_data])
            order.meta_info = "{}"
            order.total = Decimal('0.00')
            order.save()

            if ia:
                ia.order = order
                ia.save()

            pos_map = {}
            for pos_data in positions_data:
                answers_data = pos_data.pop('answers', [])
                addon_to = pos_data.pop('addon_to', None)
                attendee_name = pos_data.pop('attendee_name', '')
                if attendee_name and not pos_data.get('attendee_name_parts'):
                    pos_data['attendee_name_parts'] = {
                        '_legacy': attendee_name
                    }
                pos = OrderPosition(**pos_data)
                pos.order = order
                if addon_to:
                    pos.addon_to = pos_map[addon_to]

                if pos.price is None:
                    price = get_price(
                        item=pos.item,
                        variation=pos.variation,
                        voucher=pos.voucher,
                        custom_price=None,
                        subevent=pos.subevent,
                        addon_to=pos.addon_to,
                        invoice_address=ia,
                    )
                    pos.price = price.gross
                    pos.tax_rate = price.rate
                    pos.tax_value = price.tax
                    pos.tax_rule = pos.item.tax_rule
                else:
                    pos._calculate_tax()
                if pos.voucher:
                    Voucher.objects.filter(pk=pos.voucher.pk).update(
                        redeemed=F('redeemed') + 1)
                pos.save()
                pos_map[pos.positionid] = pos
                for answ_data in answers_data:
                    options = answ_data.pop('options', [])
                    answ = pos.answers.create(**answ_data)
                    answ.options.add(*options)

            for cp in delete_cps:
                cp.delete()

        for fee_data in fees_data:
            f = OrderFee(**fee_data)
            f.order = order
            f._calculate_tax()
            f.save()

        order.total = sum([p.price for p in order.positions.all()]) + sum(
            [f.value for f in order.fees.all()])
        order.save(update_fields=['total'])

        if order.total == Decimal(
                '0.00') and validated_data.get('status') != Order.STATUS_PAID:
            order.status = Order.STATUS_PAID
            order.save()
            order.payments.create(amount=order.total,
                                  provider='free',
                                  state=OrderPayment.PAYMENT_STATE_CONFIRMED,
                                  payment_date=now())
        elif payment_provider == "free" and order.total != Decimal('0.00'):
            raise ValidationError(
                'You cannot use the "free" payment provider for non-free orders.'
            )
        elif validated_data.get('status') == Order.STATUS_PAID:
            if not payment_provider:
                raise ValidationError(
                    'You cannot create a paid order without a payment provider.'
                )
            order.payments.create(amount=order.total,
                                  provider=payment_provider,
                                  info=payment_info,
                                  payment_date=payment_date,
                                  state=OrderPayment.PAYMENT_STATE_CONFIRMED)
        elif payment_provider:
            order.payments.create(amount=order.total,
                                  provider=payment_provider,
                                  info=payment_info,
                                  state=OrderPayment.PAYMENT_STATE_CREATED)

        return order
示例#14
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()
示例#15
0
    def create(self, validated_data):
        fees_data = validated_data.pop('fees') if 'fees' in validated_data else []
        positions_data = validated_data.pop('positions') if 'positions' in validated_data else []
        payment_provider = validated_data.pop('payment_provider')
        payment_info = validated_data.pop('payment_info', '{}')
        payment_date = validated_data.pop('payment_date', now())
        force = validated_data.pop('force', False)

        if 'invoice_address' in validated_data:
            iadata = validated_data.pop('invoice_address')
            name = iadata.pop('name', '')
            if name and not iadata.get('name_parts'):
                iadata['name_parts'] = {
                    '_legacy': name
                }
            ia = InvoiceAddress(**iadata)
        else:
            ia = None

        with self.context['event'].lock() as now_dt:
            quotadiff = Counter()

            consume_carts = validated_data.pop('consume_carts', [])
            delete_cps = []
            quota_avail_cache = {}
            if consume_carts:
                for cp in CartPosition.objects.filter(event=self.context['event'], cart_id__in=consume_carts):
                    quotas = (cp.variation.quotas.filter(subevent=cp.subevent)
                              if cp.variation else cp.item.quotas.filter(subevent=cp.subevent))
                    for quota in quotas:
                        if quota not in quota_avail_cache:
                            quota_avail_cache[quota] = list(quota.availability())
                        if quota_avail_cache[quota][1] is not None:
                            quota_avail_cache[quota][1] += 1
                    if cp.expires > now_dt:
                        quotadiff.subtract(quotas)
                    delete_cps.append(cp)

            errs = [{} for p in positions_data]

            if not force:
                for i, pos_data in enumerate(positions_data):
                    new_quotas = (pos_data.get('variation').quotas.filter(subevent=pos_data.get('subevent'))
                                  if pos_data.get('variation')
                                  else pos_data.get('item').quotas.filter(subevent=pos_data.get('subevent')))
                    if len(new_quotas) == 0:
                        errs[i]['item'] = [ugettext_lazy('The product "{}" is not assigned to a quota.').format(
                            str(pos_data.get('item'))
                        )]
                    else:
                        for quota in new_quotas:
                            if quota not in quota_avail_cache:
                                quota_avail_cache[quota] = list(quota.availability())

                            if quota_avail_cache[quota][1] is not None:
                                quota_avail_cache[quota][1] -= 1
                                if quota_avail_cache[quota][1] < 0:
                                    errs[i]['item'] = [
                                        ugettext_lazy('There is not enough quota available on quota "{}" to perform the operation.').format(
                                            quota.name
                                        )
                                    ]

                    quotadiff.update(new_quotas)

            if any(errs):
                raise ValidationError({'positions': errs})

            if validated_data.get('locale', None) is None:
                validated_data['locale'] = self.context['event'].settings.locale
            order = Order(event=self.context['event'], **validated_data)
            order.set_expires(subevents=[p.get('subevent') for p in positions_data])
            order.total = sum([p['price'] for p in positions_data]) + sum([f['value'] for f in fees_data], Decimal('0.00'))
            order.meta_info = "{}"
            order.save()

            if order.total == Decimal('0.00') and validated_data.get('status') != Order.STATUS_PAID:
                order.status = Order.STATUS_PAID
                order.save()
                order.payments.create(
                    amount=order.total, provider='free', state=OrderPayment.PAYMENT_STATE_CONFIRMED,
                    payment_date=now()
                )
            elif payment_provider == "free" and order.total != Decimal('0.00'):
                raise ValidationError('You cannot use the "free" payment provider for non-free orders.')
            elif validated_data.get('status') == Order.STATUS_PAID:
                order.payments.create(
                    amount=order.total,
                    provider=payment_provider,
                    info=payment_info,
                    payment_date=payment_date,
                    state=OrderPayment.PAYMENT_STATE_CONFIRMED
                )
            elif payment_provider:
                order.payments.create(
                    amount=order.total,
                    provider=payment_provider,
                    info=payment_info,
                    state=OrderPayment.PAYMENT_STATE_CREATED
                )

            if ia:
                ia.order = order
                ia.save()
            pos_map = {}
            for pos_data in positions_data:
                answers_data = pos_data.pop('answers', [])
                addon_to = pos_data.pop('addon_to', None)
                attendee_name = pos_data.pop('attendee_name', '')
                if attendee_name and not pos_data.get('attendee_name_parts'):
                    pos_data['attendee_name_parts'] = {
                        '_legacy': attendee_name
                    }
                pos = OrderPosition(**pos_data)
                pos.order = order
                pos._calculate_tax()
                if addon_to:
                    pos.addon_to = pos_map[addon_to]
                pos.save()
                pos_map[pos.positionid] = pos
                for answ_data in answers_data:
                    options = answ_data.pop('options', [])
                    answ = pos.answers.create(**answ_data)
                    answ.options.add(*options)

            for cp in delete_cps:
                cp.delete()
        for fee_data in fees_data:
            f = OrderFee(**fee_data)
            f.order = order
            f._calculate_tax()
            f.save()

        return order