Fix #599 -- Add API to create orders (#911)

* [WIP] Fix #599 -- Add API to create orders

* Add more validation logic

* Add docs and some validation

* Fix test on MySQl

* Validation is fun, let's do more of it!

* Fix live_issues
This commit is contained in:
Raphael Michel
2018-05-16 12:14:31 +02:00
committed by GitHub
parent 359a5d01e6
commit 35e8dcf2bc
9 changed files with 1510 additions and 43 deletions

View File

@@ -545,7 +545,7 @@ class Event(EventMixin, LoggedModel):
def has_payment_provider(self):
result = False
for provider in self.get_payment_providers().values():
if provider.is_enabled and provider.identifier != 'free':
if provider.is_enabled and provider.identifier not in ('free', 'boxoffice'):
result = True
break
return result

View File

@@ -2,7 +2,7 @@ import copy
import json
import os
import string
from datetime import datetime, time
from datetime import datetime, time, timedelta
from decimal import Decimal
from typing import Any, Dict, List, Union
@@ -218,11 +218,42 @@ class Order(LoggedModel):
self.assign_code()
if not self.datetime:
self.datetime = now()
if not self.expires:
self.set_expires()
super().save(**kwargs)
def touch(self):
self.save(update_fields=['last_modified'])
def set_expires(self, now_dt=None, subevents=None):
now_dt = now_dt or now()
tz = pytz.timezone(self.event.settings.timezone)
exp_by_date = now_dt.astimezone(tz) + timedelta(days=self.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 self.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)
self.expires = exp_by_date
term_last = self.event.settings.get('payment_term_last', as_type=RelativeDateWrapper)
if term_last:
if self.event.has_subevents and subevents:
term_last = min([
term_last.datetime(se).date()
for se in subevents
])
else:
term_last = term_last.datetime(self.event).date()
term_last = make_aware(datetime.combine(
term_last,
time(hour=23, minute=59, second=59)
), tz)
if term_last < self.expires:
self.expires = term_last
@cached_property
def tax_total(self):
return (self.positions.aggregate(s=Sum('tax_value'))['s'] or 0) + (self.fees.aggregate(s=Sum('tax_value'))['s'] or 0)

View File

@@ -658,6 +658,39 @@ class FreeOrderProvider(BasePaymentProvider):
return False
class BoxOfficeProvider(BasePaymentProvider):
is_implicit = True
is_enabled = True
identifier = "boxoffice"
verbose_name = _("Box office")
def payment_perform(self, request: HttpRequest, order: Order):
from pretix.base.services.orders import mark_order_paid
try:
mark_order_paid(order, 'boxoffice', send_mail=False)
except Quota.QuotaExceededException as e:
raise PaymentException(str(e))
@property
def settings_form_fields(self) -> dict:
return {}
def order_control_refund_render(self, order: Order) -> str:
return ''
def order_control_refund_perform(self, request: HttpRequest, order: Order) -> Union[bool, str]:
from pretix.base.services.orders import mark_order_refunded
mark_order_refunded(order, user=request.user)
messages.success(request, _('The order has been marked as refunded.'))
def is_allowed(self, request: HttpRequest) -> bool:
return False
def order_change_allowed(self, order: Order) -> bool:
return False
@receiver(register_payment_providers, dispatch_uid="payment_free")
def register_payment_provider(sender, **kwargs):
return FreeOrderProvider
return [FreeOrderProvider, BoxOfficeProvider]

View File

@@ -13,7 +13,7 @@ from django.db import transaction
from django.db.models import F, Max, Q, Sum
from django.dispatch import receiver
from django.utils.formats import date_format
from django.utils.timezone import make_aware, now
from django.utils.timezone import now
from django.utils.translation import ugettext as _
from pretix.base.i18n import (
@@ -31,7 +31,6 @@ from pretix.base.models.orders import (
from pretix.base.models.organizer import TeamAPIToken
from pretix.base.models.tax import TaxedPrice
from pretix.base.payment import BasePaymentProvider
from pretix.base.reldate import RelativeDateWrapper
from pretix.base.services.async import ProfiledTask
from pretix.base.services.invoices import (
generate_cancellation, generate_invoice, invoice_qualified,
@@ -448,50 +447,22 @@ def _get_fees(positions: List[CartPosition], payment_provider: BasePaymentProvid
def _create_order(event: Event, email: str, positions: List[CartPosition], now_dt: datetime,
payment_provider: BasePaymentProvider, locale: str=None, address: InvoiceAddress=None,
meta_info: dict=None):
from datetime import time
fees = _get_fees(positions, payment_provider, address, meta_info, event)
total = sum([c.price for c in positions]) + sum([c.value for c in fees])
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(
order = Order(
status=Order.STATUS_PENDING,
event=event,
email=email,
datetime=now_dt,
expires=expires,
locale=locale,
total=total,
payment_provider=payment_provider.identifier,
meta_info=json.dumps(meta_info or {}),
)
order.set_expires(now_dt, event.subevents.filter(id__in=[p.subevent_id for p in positions]))
order.save()
if address:
if address.order is not None: