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
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
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
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
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
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