def mark_order_paid(order: Order, provider: str=None, info: str=None, date: datetime=None, manual: bool=None, force: bool=False, send_mail: bool=True, user: User=None) -> Order: """ Marks an order as paid. This sets the payment provider, info and date and returns the order object. :param provider: The payment provider that marked this as paid :type provider: str :param info: The information to store in order.payment_info :type info: str :param date: The date the payment was received (if you pass ``None``, the current time will be used). :type date: datetime :param force: Whether this payment should be marked as paid even if no remaining quota is available (default: ``False``). :type force: boolean :param send_mail: Whether an email should be sent to the user about this event (default: ``True``). :type send_mail: boolean :param user: The user that performed the change :raises Quota.QuotaExceededException: if the quota is exceeded and ``force`` is ``False`` """ with order.event.lock() as now_dt: can_be_paid = order._can_be_paid() if not force and can_be_paid is not True: raise Quota.QuotaExceededException(can_be_paid) order.payment_provider = provider or order.payment_provider order.payment_info = info or order.payment_info order.payment_date = date or now_dt if manual is not None: order.payment_manual = manual order.status = Order.STATUS_PAID order.save() order.log_action('pretix.event.order.paid', { 'provider': provider, 'info': info, 'date': date, 'manual': manual, 'force': force }, user=user) order_paid.send(order.event, order=order) if send_mail: with language(order.locale): mail( order.email, _('Payment received for your order: %(code)s') % {'code': order.code}, order.event.settings.mail_text_order_paid, { 'event': order.event.name, 'url': build_absolute_uri(order.event, 'presale:event.order', kwargs={ 'order': order.code, 'secret': order.secret }), 'downloads': order.event.settings.get('ticket_download', as_type=bool) }, order.event, locale=order.locale ) return order
def mark_order_paid(order: Order, provider: str=None, info: str=None, date: datetime=None, manual: bool=None, force: bool=False, send_mail: bool=True, user: User=None) -> Order: """ Marks an order as paid. This sets the payment provider, info and date and returns the order object. :param provider: The payment provider that marked this as paid :type provider: str :param info: The information to store in order.payment_info :type info: str :param date: The date the payment was received (if you pass ``None``, the current time will be used). :type date: datetime :param force: Whether this payment should be marked as paid even if no remaining quota is available (default: ``False``). :type force: boolean :param send_mail: Whether an email should be sent to the user about this event (default: ``True``). :type send_mail: boolean :param user: The user that performed the change :raises Quota.QuotaExceededException: if the quota is exceeded and ``force`` is ``False`` """ with order.event.lock(): can_be_paid = order._can_be_paid() if not force and can_be_paid is not True: raise Quota.QuotaExceededException(can_be_paid) order.payment_provider = provider or order.payment_provider order.payment_info = info or order.payment_info order.payment_date = date or now() if manual is not None: order.payment_manual = manual order.status = Order.STATUS_PAID order.save() order.log_action('pretix.event.order.paid', { 'provider': provider, 'info': info, 'date': date, 'manual': manual, 'force': force }, user=user) order_paid.send(order.event, order=order) if send_mail: with language(order.locale): mail( order.email, _('Payment received for your order: %(code)s') % {'code': order.code}, order.event.settings.mail_text_order_paid, { 'event': order.event.name, 'url': build_absolute_uri(order.event, 'presale:event.order', kwargs={ 'order': order.code, 'secret': order.secret }), 'downloads': order.event.settings.get('ticket_download', as_type=bool) }, order.event, locale=order.locale ) return order
def mark_order_paid(order: Order, provider: str = None, info: str = None, date: datetime = None, manual: bool = None, force: bool = False): """ Marks an order as paid. This clones the order object, sets the payment provider, info and date and returns the cloned order object. :param provider: The payment provider that marked this as paid :type provider: str :param info: The information to store in order.payment_info :type info: str :param date: The date the payment was received (if you pass ``None``, the current time will be used). :type date: datetime :param force: Whether this payment should be marked as paid even if no remaining quota is available (default: ``False``). :type force: boolean :raises Quota.QuotaExceededException: if the quota is exceeded and ``force`` is ``False`` """ with order.event.lock(): can_be_paid = order._can_be_paid() if not force and can_be_paid is not True: raise Quota.QuotaExceededException(can_be_paid) order = order.clone() order.payment_provider = provider or order.payment_provider order.payment_info = info or order.payment_info order.payment_date = date or now() if manual is not None: order.payment_manual = manual order.status = Order.STATUS_PAID order.save() order_paid.send(order.event, order=order) mail(order.email, _('Payment received for your order: %(code)s') % {'code': order.code}, 'pretixpresale/email/order_paid.txt', { 'order': order, 'event': order.event, 'url': build_absolute_uri(order.event, 'presale:event.order', kwargs={ 'order': order.code, 'secret': order.secret }), 'downloads': order.event.settings.get('ticket_download', as_type=bool) }, order.event, locale=order.locale) return order
def mark_order_paid(order, provider=None, info=None, date=None, manual=None, force=False): """ Marks an order as paid. This clones the order object, sets the payment provider, info and date and returns the cloned order object. :param provider: The payment provider that marked this as paid :type provider: str :param info: The information to store in order.payment_info :type info: str :param date: The date the payment was received (if you pass ``None``, the current time will be used). :type date: datetime :param force: Whether this payment should be marked as paid even if no remaining quota is available (default: ``False``). :type force: boolean :raises Quota.QuotaExceededException: if the quota is exceeded and ``force`` is ``False`` """ can_be_paid, quotas_locked = order._can_be_paid(keep_locked=True) if not force and can_be_paid is not True: raise Quota.QuotaExceededException(can_be_paid) order = order.clone() order.payment_provider = provider or order.payment_provider order.payment_info = info or order.payment_info order.payment_date = date or now() if manual is not None: order.payment_manual = manual order.status = Order.STATUS_PAID order.save() order_paid.send(order.event, order=order) if quotas_locked: for quota in quotas_locked: quota.release() from pretix.base.services.mail import mail mail( order.user, _('Payment received for your order: %(code)s') % {'code': order.code}, 'pretixpresale/email/order_paid.txt', { 'user': order.user, 'order': order, 'event': order.event, 'url': build_absolute_uri('presale:event.order', kwargs={ 'event': order.event.slug, 'organizer': order.event.organizer.slug, 'order': order.code, }), 'downloads': order.event.settings.get('ticket_download', as_type=bool) }, order.event ) return order
def mark_order_paid(order: Order, provider: str=None, info: str=None, date: datetime=None, manual: bool=None, force: bool=False, send_mail: bool=True, user: User=None, mail_text='') -> Order: """ Marks an order as paid. This sets the payment provider, info and date and returns the order object. :param provider: The payment provider that marked this as paid :type provider: str :param info: The information to store in order.payment_info :type info: str :param date: The date the payment was received (if you pass ``None``, the current time will be used). :type date: datetime :param force: Whether this payment should be marked as paid even if no remaining quota is available (default: ``False``). :type force: boolean :param send_mail: Whether an email should be sent to the user about this event (default: ``True``). :type send_mail: boolean :param user: The user that performed the change :param mail_text: Additional text to be included in the email :type mail_text: str :raises Quota.QuotaExceededException: if the quota is exceeded and ``force`` is ``False`` """ if order.status == Order.STATUS_PAID: return order with order.event.lock() as now_dt: can_be_paid = order._can_be_paid() if not force and can_be_paid is not True: raise Quota.QuotaExceededException(can_be_paid) order.payment_provider = provider or order.payment_provider order.payment_info = info or order.payment_info order.payment_date = date or now_dt if manual is not None: order.payment_manual = manual order.status = Order.STATUS_PAID order.save() order.log_action('pretix.event.order.paid', { 'provider': provider, 'info': info, 'date': date or now_dt, 'manual': manual, 'force': force }, user=user) order_paid.send(order.event, order=order) if order.event.settings.get('invoice_generate') in ('True', 'paid') and invoice_qualified(order): if not order.invoices.exists(): generate_invoice(order) if send_mail: with language(order.locale): try: invoice_name = order.invoice_address.name invoice_company = order.invoice_address.company except InvoiceAddress.DoesNotExist: invoice_name = "" invoice_company = "" email_template = order.event.settings.mail_text_order_paid email_context = { 'event': order.event.name, 'url': build_absolute_uri(order.event, 'presale:event.order', kwargs={ 'order': order.code, 'secret': order.secret }), 'downloads': order.event.settings.get('ticket_download', as_type=bool), 'invoice_name': invoice_name, 'invoice_company': invoice_company, 'payment_info': mail_text } email_subject = _('Payment received for your order: %(code)s') % {'code': order.code} try: order.send_mail( email_subject, email_template, email_context, 'pretix.event.order.email.order_paid', user ) except SendMailException: logger.exception('Order paid email could not be sent') return order
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()
def schedule_second_dose(self, event, op): op = OrderPosition.objects.select_related("item", "variation", "subevent", "order").get(pk=op) if LinkedOrderPosition.objects.filter(base_position=op).exists(): return itemconf = op.item.vacc_autosched_config earliest_date = make_aware( datetime.combine( op.subevent.date_from.astimezone(event.timezone).date() + timedelta(days=itemconf.days), op.subevent.date_from.astimezone(event.timezone).time(), ), event.timezone, ) target_event = itemconf.event or event possible_items = [ n for n in target_event.items.all() if (n.internal_name or str(n.name)) == ( op.item.internal_name or str(op.item.name)) ] if len(possible_items) != 1: op.order.log_action( "pretix_vacc_autosched.failed", data={ "reason": _("No product found"), "position": op.pk, }, ) return target_item = possible_items[0] if op.variation or target_item.variations.exists(): possible_variations = [ n for n in target_item.variations.all() if str(n.value) == ( str(op.variation.value) if op.variation else None) ] if len(possible_variations) != 1: op.order.log_action( "pretix_vacc_autosched.failed", data={ "reason": _("No product variation found"), "position": op.pk, }, ) return target_var = possible_variations[0] else: target_var = None for i in range(100): # max number of subevents to check subevent = (target_event.subevents.filter( date_from__gte=earliest_date, ).order_by("date_from").first()) if not subevent: op.order.log_action( "pretix_vacc_autosched.failed", data={ "reason": _("No available time slot found"), "position": op.pk, }, ) return try: with target_event.lock(), transaction.atomic(): avcode, avnr = target_item.check_quotas(subevent=subevent, fail_on_no_quotas=True) if avcode != Quota.AVAILABILITY_OK: # sold out, look for next one earliest_date += timedelta(minutes=1) continue childorder = Order.objects.create( event=target_event, status=Order.STATUS_PAID, require_approval=False, testmode=op.order.testmode, email=op.order.email, locale=op.order.locale, expires=now() + timedelta(days=30), total=Decimal("0.00"), expiry_reminder_sent=True, sales_channel=op.order.sales_channel, comment="Auto-generated through scheduling from order {}". format(op.order.code), meta_info=op.order.meta_info, ) op.order.log_action( "pretix_vacc_autosched.scheduled", data={ "position": op.pk, "event": target_event.pk, "event_slug": target_event.slug, "order": childorder.code, }, ) childorder.log_action( "pretix_vacc_autosched.created", data={ "event": event.pk, "event_slug": event.slug, "order": op.order.code, }, ) childorder.log_action("pretix.event.order.placed", data={"source": "vacc_autosched"}) childpos = childorder.positions.create( positionid=1, tax_rate=Decimal("0.00"), tax_rule=None, tax_value=Decimal("0.00"), subevent=subevent, item=target_item, variation=target_var, price=Decimal("0.00"), attendee_name_cached=op.attendee_name_cached, attendee_name_parts=op.attendee_name_parts, attendee_email=op.attendee_email, company=op.company, street=op.street, zipcode=op.zipcode, city=op.city, country=op.country, state=op.state, addon_to=None, voucher=None, meta_info=op.meta_info, ) for answ in op.answers.all(): q = childorder.event.questions.filter( identifier=answ.question.identifier).first() if not q: continue childansw = childpos.answers.create(question=q, answer=answ.answer, file=answ.file) if answ.options.all(): childopts = list( q.options.filter(identifier__in=[ o.identifier for o in answ.options.all() ])) if childopts: childansw.options.add(*childopts) LinkedOrderPosition.objects.create(base_position=op, child_position=childpos) order_placed.send(event, order=childorder) order_paid.send(event, order=childorder) if event.settings.vacc_autosched_mail: with language(childorder.locale, target_event.settings.region): email_template = event.settings.vacc_autosched_body email_subject = str(event.settings.vacc_autosched_subject) email_context = get_email_context(event=childorder.event, order=childorder) email_context["scheduled_datetime"] = date_format( subevent.date_from.astimezone(event.timezone), "SHORT_DATETIME_FORMAT", ) try: childorder.send_mail( email_subject, email_template, email_context, "pretix.event.order.email.order_placed", attach_tickets=True, attach_ical=target_event.settings.mail_attach_ical, ) except SendMailException: logger.exception( "Order approved email could not be sent") except LockTimeoutException: self.retry() return op.order.log_action( "pretix_vacc_autosched.failed", data={ "reason": _("No available time slot found"), "position": op.pk, }, ) return
def book_second_dose(*, op, item, variation, subevent, original_event): event = item.event with event.lock(), transaction.atomic(): avcode, avnr = item.check_quotas(subevent=subevent, fail_on_no_quotas=True) if avcode != Quota.AVAILABILITY_OK: logger.info( f"SECOND DOSE: cannot use slot {subevent.pk}, sold out") # sold out, look for next one return childorder = Order.objects.create( event=event, status=Order.STATUS_PAID, require_approval=False, testmode=op.order.testmode, email=op.order.email, phone=op.order.phone, locale=op.order.locale, expires=now() + timedelta(days=30), total=Decimal("0.00"), expiry_reminder_sent=True, sales_channel=op.order.sales_channel, comment="Auto-generated through scheduling from order {}".format( op.order.code), meta_info=op.order.meta_info, ) op.order.log_action( "pretix_vacc_autosched.scheduled", data={ "position": op.pk, "event": event.pk, "event_slug": event.slug, "order": childorder.code, }, ) childorder.log_action( "pretix_vacc_autosched.created", data={ "event": original_event.pk, "event_slug": original_event.slug, "order": op.order.code, }, ) childorder.log_action("pretix.event.order.placed", data={"source": "vacc_autosched"}) childpos = childorder.positions.create( positionid=1, tax_rate=Decimal("0.00"), tax_rule=None, tax_value=Decimal("0.00"), subevent=subevent, item=item, variation=variation, price=Decimal("0.00"), attendee_name_cached=op.attendee_name_cached, attendee_name_parts=op.attendee_name_parts, attendee_email=op.attendee_email, company=op.company, street=op.street, zipcode=op.zipcode, city=op.city, country=op.country, state=op.state, addon_to=None, voucher=None, meta_info=op.meta_info, ) for answ in op.answers.all(): q = childorder.event.questions.filter( identifier=answ.question.identifier).first() if not q: continue childansw = childpos.answers.create(question=q, answer=answ.answer, file=answ.file) if answ.options.all(): childopts = list( q.options.filter(identifier__in=[ o.identifier for o in answ.options.all() ])) if childopts: childansw.options.add(*childopts) LinkedOrderPosition.objects.create(base_position=op, child_position=childpos) childorder.create_transactions(is_new=True) order_placed.send(event, order=childorder) order_paid.send(event, order=childorder) if original_event.settings.vacc_autosched_mail: with language(childorder.locale, original_event.settings.region): email_template = original_event.settings.vacc_autosched_body email_subject = str(original_event.settings.vacc_autosched_subject) email_context = get_email_context(event=childorder.event, order=childorder) email_context["scheduled_datetime"] = date_format( subevent.date_from.astimezone(original_event.timezone), "SHORT_DATETIME_FORMAT", ) try: childorder.send_mail( email_subject, email_template, email_context, "pretix.event.order.email.order_placed", attach_tickets=True, attach_ical=event.settings.mail_attach_ical, ) except SendMailException: logger.exception("Order approved email could not be sent") if (original_event.settings.vacc_autosched_sms and can_use_juvare_api(original_event) and childorder.phone): with language(childorder.locale, original_event.settings.region): from pretix_juvare_notify.tasks import juvare_send_text template = original_event.settings.vacc_autosched_sms_text context = get_email_context(event=childorder.event, order=childorder) context["scheduled_datetime"] = date_format( subevent.date_from.astimezone(original_event.timezone), "SHORT_DATETIME_FORMAT", ) message = str(template).format_map(TolerantDict(context)) juvare_send_text.apply_async( kwargs={ "text": message, "to": childorder.phone, "event": original_event.pk, }) logger.info(f"SECOND DOSE: done, created order {childorder.code}") return childorder