Merge pull request #989 from pretix/approvals

Require approval for orders of specific products
This commit is contained in:
Raphael Michel
2018-08-14 17:12:32 +02:00
committed by GitHub
40 changed files with 955 additions and 85 deletions

View File

@@ -237,7 +237,7 @@ def invoice_pdf_task(invoice: int):
def invoice_qualified(order: Order):
if order.total == Decimal('0.00'):
if order.total == Decimal('0.00') or order.require_approval:
return False
return True

View File

@@ -169,6 +169,142 @@ def mark_order_expired(order, user=None, auth=None):
return order
@transaction.atomic
def approve_order(order, user=None, send_mail: bool=True, auth=None):
"""
Mark this order as approved
:param order: The order to change
:param user: The user that performed the change
"""
if not order.require_approval or not order.status == Order.STATUS_PENDING:
raise OrderError(_('This order is not pending approval.'))
order.require_approval = False
order.set_expires(now(), order.event.subevents.filter(id__in=[p.subevent_id for p in order.positions.all()]))
order.save()
order.log_action('pretix.event.order.approved', user=user, auth=auth)
if order.total == Decimal('0.00'):
p = order.payments.create(
state=OrderPayment.PAYMENT_STATE_CREATED,
provider='free',
amount=0,
fee=None
)
try:
p.confirm(send_mail=False, count_waitinglist=False, user=user, auth=auth)
except Quota.QuotaExceededException:
raise OrderError(error_messages['unavailable'])
invoice = order.invoices.last() # Might be generated by plugin already
if order.event.settings.get('invoice_generate') == 'True' and invoice_qualified(order):
if not invoice:
generate_invoice(
order,
trigger_pdf=not order.event.settings.invoice_email_attachment or not order.email
)
# send_mail will trigger PDF generation later
if send_mail:
try:
invoice_name = order.invoice_address.name
invoice_company = order.invoice_address.company
except InvoiceAddress.DoesNotExist:
invoice_name = ""
invoice_company = ""
with language(order.locale):
if order.total == Decimal('0.00'):
email_template = order.event.settings.mail_text_order_free
email_subject = _('Order approved and confirmed: %(code)s') % {'code': order.code}
else:
email_template = order.event.settings.mail_text_order_approved
email_subject = _('Order approved and awaiting payment: %(code)s') % {'code': order.code}
email_context = {
'total': LazyNumber(order.total),
'currency': order.event.currency,
'total_with_currency': LazyCurrencyNumber(order.total, order.event.currency),
'date': LazyDate(order.expires),
'event': order.event.name,
'url': build_absolute_uri(order.event, 'presale:event.order', kwargs={
'order': order.code,
'secret': order.secret
}),
'invoice_name': invoice_name,
'invoice_company': invoice_company,
}
try:
order.send_mail(
email_subject, email_template, email_context,
'pretix.event.order.email.order_approved', user
)
except SendMailException:
logger.exception('Order approved email could not be sent')
return order.pk
@transaction.atomic
def deny_order(order, comment='', user=None, send_mail: bool=True, auth=None):
"""
Mark this order as canceled
:param order: The order to change
:param user: The user that performed the change
"""
if not order.require_approval or not order.status == Order.STATUS_PENDING:
raise OrderError(_('This order is not pending approval.'))
with order.event.lock():
order.status = Order.STATUS_CANCELED
order.save()
order.log_action('pretix.event.order.denied', user=user, auth=auth, data={
'comment': comment
})
i = order.invoices.filter(is_cancellation=False).last()
if i:
generate_cancellation(i)
for position in order.positions.all():
if position.voucher:
Voucher.objects.filter(pk=position.voucher.pk).update(redeemed=F('redeemed') - 1)
if send_mail:
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_denied
email_context = {
'total': LazyNumber(order.total),
'currency': order.event.currency,
'total_with_currency': LazyCurrencyNumber(order.total, order.event.currency),
'date': LazyDate(order.expires),
'event': order.event.name,
'url': build_absolute_uri(order.event, 'presale:event.order', kwargs={
'order': order.code,
'secret': order.secret
}),
'comment': comment,
'invoice_name': invoice_name,
'invoice_company': invoice_company,
}
with language(order.locale):
email_subject = _('Order denied: %(code)s') % {'code': order.code}
try:
order.send_mail(
email_subject, email_template, email_context,
'pretix.event.order.email.order_denied', user
)
except SendMailException:
logger.exception('Order denied email could not be sent')
return order.pk
@transaction.atomic
def _cancel_order(order, user=None, send_mail: bool=True, api_token=None, oauth_application=None):
"""
@@ -342,7 +478,10 @@ def _get_fees(positions: List[CartPosition], payment_provider: BasePaymentProvid
meta_info: dict, event: Event):
fees = []
total = sum([c.price for c in positions])
payment_fee = payment_provider.calculate_fee(total)
if payment_provider:
payment_fee = payment_provider.calculate_fee(total)
else:
payment_fee = 0
pf = None
if payment_fee:
pf = OrderFee(fee_type=OrderFee.FEE_TYPE_PAYMENT, value=payment_fee,
@@ -370,6 +509,7 @@ def _create_order(event: Event, email: str, positions: List[CartPosition], now_d
locale=locale,
total=total,
meta_info=json.dumps(meta_info or {}),
require_approval=any(p.item.require_approval for p in positions)
)
order.set_expires(now_dt, event.subevents.filter(id__in=[p.subevent_id for p in positions]))
order.save()
@@ -389,12 +529,13 @@ def _create_order(event: Event, email: str, positions: List[CartPosition], now_d
fee.tax_rule = None # TODO: deprecate
fee.save()
order.payments.create(
state=OrderPayment.PAYMENT_STATE_CREATED,
provider=payment_provider,
amount=total,
fee=pf
)
if payment_provider:
order.payments.create(
state=OrderPayment.PAYMENT_STATE_CREATED,
provider=payment_provider,
amount=total,
fee=pf
)
OrderPosition.transform_cart_positions(positions, order)
order.log_action('pretix.event.order.placed')
@@ -410,9 +551,12 @@ def _perform_order(event: str, payment_provider: str, position_ids: List[str],
email: str, locale: str, address: int, meta_info: dict=None):
event = Event.objects.get(id=event)
pprov = event.get_payment_providers().get(payment_provider)
if not pprov:
raise OrderError(error_messages['internal'])
if payment_provider:
pprov = event.get_payment_providers().get(payment_provider)
if not pprov:
raise OrderError(error_messages['internal'])
else:
pprov = None
if email == settings.PRETIX_EMAIL_NONE_VALUE:
email = None
@@ -445,7 +589,10 @@ def _perform_order(event: str, payment_provider: str, position_ids: List[str],
# send_mail will trigger PDF generation later
if order.email:
if payment_provider == 'free':
if order.require_approval:
email_template = event.settings.mail_text_order_placed_require_approval
log_entry = 'pretix.event.order.email.order_placed_require_approval'
elif payment_provider == 'free':
email_template = event.settings.mail_text_order_free
log_entry = 'pretix.event.order.email.order_free'
else:
@@ -458,6 +605,12 @@ def _perform_order(event: str, payment_provider: str, position_ids: List[str],
except InvoiceAddress.DoesNotExist:
invoice_name = ""
invoice_company = ""
if pprov:
payment_info = str(pprov.order_pending_mail_render(order))
else:
payment_info = None
email_context = {
'total': LazyNumber(order.total),
'currency': event.currency,
@@ -468,7 +621,7 @@ def _perform_order(event: str, payment_provider: str, position_ids: List[str],
'order': order.code,
'secret': order.secret
}),
'payment_info': str(pprov.order_pending_mail_render(order)),
'payment_info': payment_info,
'invoice_name': invoice_name,
'invoice_company': invoice_company,
}
@@ -489,7 +642,8 @@ def _perform_order(event: str, payment_provider: str, position_ids: List[str],
def expire_orders(sender, **kwargs):
eventcache = {}
for o in Order.objects.filter(expires__lt=now(), status=Order.STATUS_PENDING).select_related('event'):
for o in Order.objects.filter(expires__lt=now(), status=Order.STATUS_PENDING,
require_approval=False).select_related('event'):
expire = eventcache.get(o.event.pk, None)
if expire is None:
expire = o.event.settings.get('payment_term_expire_automatically', as_type=bool)