mirror of
https://github.com/pretix/pretix.git
synced 2026-02-23 09:32:27 +00:00
Proof of concept
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
# Generated by Django 2.2.1 on 2019-09-10 20:20
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
import pretix.base.models.fields
|
||||
import pretix.base.models.giftcards
|
||||
|
||||
|
||||
@@ -888,6 +888,21 @@ class OffsettingProvider(BasePaymentProvider):
|
||||
return _('Balanced against orders: %s' % ', '.join(payment.info_data['orders']))
|
||||
|
||||
|
||||
class GiftCardPayment(BasePaymentProvider):
|
||||
is_enabled = True
|
||||
identifier = "giftcard"
|
||||
verbose_name = _("Gift card")
|
||||
is_implicit = True
|
||||
|
||||
def is_allowed(self, request: HttpRequest, total: Decimal=None) -> bool:
|
||||
return False
|
||||
|
||||
def order_change_allowed(self, order: Order) -> bool:
|
||||
return False
|
||||
|
||||
# TODO: execute, refund, api, control render
|
||||
|
||||
|
||||
@receiver(register_payment_providers, dispatch_uid="payment_free")
|
||||
def register_payment_provider(sender, **kwargs):
|
||||
return [FreeOrderProvider, BoxOfficeProvider, OffsettingProvider, ManualPayment]
|
||||
return [FreeOrderProvider, BoxOfficeProvider, OffsettingProvider, ManualPayment, GiftCardPayment]
|
||||
|
||||
@@ -14,8 +14,8 @@ from django_scopes import scopes_disabled
|
||||
|
||||
from pretix.base.i18n import language
|
||||
from pretix.base.models import (
|
||||
CartPosition, Event, InvoiceAddress, Item, ItemBundle, ItemVariation, Seat,
|
||||
SeatCategoryMapping, Voucher,
|
||||
CartPosition, Event, GiftCard, InvoiceAddress, Item, ItemBundle,
|
||||
ItemVariation, Seat, SeatCategoryMapping, Voucher,
|
||||
)
|
||||
from pretix.base.models.event import SubEvent
|
||||
from pretix.base.models.orders import OrderFee
|
||||
@@ -958,14 +958,36 @@ def update_tax_rates(event: Event, cart_id: str, invoice_address: InvoiceAddress
|
||||
|
||||
|
||||
def get_fees(event, request, total, invoice_address, provider):
|
||||
fees = []
|
||||
from pretix.presale.views.cart import cart_session
|
||||
|
||||
fees = []
|
||||
for recv, resp in fee_calculation_for_cart.send(sender=event, request=request, invoice_address=invoice_address,
|
||||
total=total):
|
||||
if resp:
|
||||
fees += resp
|
||||
|
||||
total = total + sum(f.value for f in fees)
|
||||
|
||||
cs = cart_session(request)
|
||||
if cs.get('gift_cards'):
|
||||
gc_qs = GiftCard.objects.filter(pk__in=cs.get('gift_cards'))
|
||||
summed = 0
|
||||
for gc in gc_qs:
|
||||
fval = Decimal(gc.value) # TODO: don't require an extra query
|
||||
fval = min(fval, total - summed)
|
||||
if fval > 0:
|
||||
total -= fval
|
||||
summed += fval
|
||||
fees.append(OrderFee(
|
||||
fee_type=OrderFee.FEE_TYPE_GIFTCARD,
|
||||
internal_type='giftcard',
|
||||
description=gc.secret,
|
||||
value=-1 * fval,
|
||||
tax_rate=Decimal('0.00'),
|
||||
tax_value=Decimal('0.00'),
|
||||
tax_rule=TaxRule.zero()
|
||||
))
|
||||
|
||||
if provider and total != 0:
|
||||
provider = event.get_payment_providers().get(provider)
|
||||
if provider:
|
||||
|
||||
@@ -20,8 +20,9 @@ from pretix.api.models import OAuthApplication
|
||||
from pretix.base.email import get_email_context
|
||||
from pretix.base.i18n import LazyLocaleException, language
|
||||
from pretix.base.models import (
|
||||
CartPosition, Device, Event, Item, ItemVariation, Order, OrderPayment,
|
||||
OrderPosition, Quota, Seat, SeatCategoryMapping, User, Voucher,
|
||||
CartPosition, Device, Event, GiftCard, Item, ItemVariation, Order,
|
||||
OrderPayment, OrderPosition, Quota, Seat, SeatCategoryMapping, User,
|
||||
Voucher,
|
||||
)
|
||||
from pretix.base.models.event import SubEvent
|
||||
from pretix.base.models.items import ItemBundle
|
||||
@@ -545,7 +546,7 @@ def _check_positions(event: Event, now_dt: datetime, positions: List[CartPositio
|
||||
|
||||
|
||||
def _get_fees(positions: List[CartPosition], payment_provider: BasePaymentProvider, address: InvoiceAddress,
|
||||
meta_info: dict, event: Event):
|
||||
meta_info: dict, event: Event, gift_cards: List[GiftCard]):
|
||||
fees = []
|
||||
total = sum([c.price for c in positions])
|
||||
|
||||
@@ -553,8 +554,18 @@ def _get_fees(positions: List[CartPosition], payment_provider: BasePaymentProvid
|
||||
meta_info=meta_info, positions=positions):
|
||||
if resp:
|
||||
fees += resp
|
||||
|
||||
total += sum(f.value for f in fees)
|
||||
|
||||
summed = 0
|
||||
gift_card_values = {}
|
||||
for gc in gift_cards:
|
||||
fval = Decimal(gc.value) # TODO: don't require an extra query
|
||||
fval = min(fval, total - summed)
|
||||
if fval > 0:
|
||||
total -= fval
|
||||
summed += fval
|
||||
gift_card_values[gc] = fval
|
||||
|
||||
if payment_provider:
|
||||
payment_fee = payment_provider.calculate_fee(total)
|
||||
else:
|
||||
@@ -565,17 +576,24 @@ def _get_fees(positions: List[CartPosition], payment_provider: BasePaymentProvid
|
||||
internal_type=payment_provider.identifier)
|
||||
fees.append(pf)
|
||||
|
||||
return fees, pf
|
||||
return fees, pf, gift_card_values
|
||||
|
||||
|
||||
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, sales_channel: str='web'):
|
||||
fees, pf = _get_fees(positions, payment_provider, address, meta_info, event)
|
||||
total = sum([c.price for c in positions]) + sum([c.value for c in fees])
|
||||
meta_info: dict=None, sales_channel: str='web', gift_cards: list=None):
|
||||
p = None
|
||||
|
||||
with transaction.atomic():
|
||||
checked_gift_cards = []
|
||||
if gift_cards:
|
||||
gc_qs = GiftCard.objects.select_for_update().filter(pk__in=gift_cards) # TODO: Make sure to prevent race conditions
|
||||
for gc in gc_qs:
|
||||
# TODO: Re-check acceptance
|
||||
checked_gift_cards.append(gc)
|
||||
|
||||
fees, pf, gift_card_values = _get_fees(positions, payment_provider, address, meta_info, event, checked_gift_cards)
|
||||
total = pending_sum = sum([c.price for c in positions]) + sum([c.value for c in fees])
|
||||
|
||||
order = Order(
|
||||
status=Order.STATUS_PENDING,
|
||||
event=event,
|
||||
@@ -606,11 +624,30 @@ def _create_order(event: Event, email: str, positions: List[CartPosition], now_d
|
||||
fee.tax_rule = None # TODO: deprecate
|
||||
fee.save()
|
||||
|
||||
for gc, val in gift_card_values.items():
|
||||
p = order.payments.create(
|
||||
state=OrderPayment.PAYMENT_STATE_CONFIRMED,
|
||||
provider='giftcard',
|
||||
amount=val,
|
||||
fee=pf
|
||||
)
|
||||
trans = gc.transactions.create(
|
||||
value=-1 * val,
|
||||
order=order,
|
||||
payment=p
|
||||
)
|
||||
p.info_data = {
|
||||
'gift_card': gc.pk,
|
||||
'transaction_id': trans.pk,
|
||||
}
|
||||
p.save()
|
||||
pending_sum -= val
|
||||
|
||||
if payment_provider and not order.require_approval:
|
||||
p = order.payments.create(
|
||||
state=OrderPayment.PAYMENT_STATE_CREATED,
|
||||
provider=payment_provider.identifier,
|
||||
amount=total,
|
||||
amount=pending_sum,
|
||||
fee=pf
|
||||
)
|
||||
|
||||
@@ -658,7 +695,8 @@ def _order_placed_email_attendee(event: Event, order: Order, position: OrderPosi
|
||||
|
||||
|
||||
def _perform_order(event: Event, payment_provider: str, position_ids: List[str],
|
||||
email: str, locale: str, address: int, meta_info: dict=None, sales_channel: str='web'):
|
||||
email: str, locale: str, address: int, meta_info: dict=None, sales_channel: str='web',
|
||||
gift_cards: list=None):
|
||||
if payment_provider:
|
||||
pprov = event.get_payment_providers().get(payment_provider)
|
||||
if not pprov:
|
||||
@@ -707,9 +745,10 @@ def _perform_order(event: Event, payment_provider: str, position_ids: List[str],
|
||||
raise OrderError(error_messages['internal'])
|
||||
_check_positions(event, now_dt, positions, address=addr)
|
||||
order, payment = _create_order(event, email, positions, now_dt, pprov,
|
||||
locale=locale, address=addr, meta_info=meta_info, sales_channel=sales_channel)
|
||||
locale=locale, address=addr, meta_info=meta_info, sales_channel=sales_channel,
|
||||
gift_cards=gift_cards)
|
||||
|
||||
free_order_flow = payment and payment_provider == 'free' and order.total == Decimal('0.00') and not order.require_approval
|
||||
free_order_flow = payment and payment_provider == 'free' and order.pending_sum == Decimal('0.00') and not order.require_approval
|
||||
if free_order_flow:
|
||||
try:
|
||||
payment.confirm(send_mail=False, lock=not locked)
|
||||
@@ -1466,12 +1505,12 @@ class OrderChangeManager:
|
||||
@app.task(base=ProfiledEventTask, bind=True, max_retries=5, default_retry_delay=1, throws=(OrderError,))
|
||||
def perform_order(self, event: Event, payment_provider: str, positions: List[str],
|
||||
email: str=None, locale: str=None, address: int=None, meta_info: dict=None,
|
||||
sales_channel: str='web'):
|
||||
sales_channel: str='web', gift_cards: list=None):
|
||||
with language(locale):
|
||||
try:
|
||||
try:
|
||||
return _perform_order(event, payment_provider, positions, email, locale, address, meta_info,
|
||||
sales_channel)
|
||||
sales_channel, gift_cards)
|
||||
except LockTimeoutException:
|
||||
self.retry()
|
||||
except (MaxRetriesExceededError, LockTimeoutException):
|
||||
|
||||
@@ -15,7 +15,7 @@ from django.utils.translation import (
|
||||
from django.views.generic.base import TemplateResponseMixin
|
||||
from django_scopes import scopes_disabled
|
||||
|
||||
from pretix.base.models import Order
|
||||
from pretix.base.models import GiftCard, Order
|
||||
from pretix.base.models.orders import InvoiceAddress, OrderPayment
|
||||
from pretix.base.services.cart import (
|
||||
get_fees, set_cart_addons, update_tax_rates,
|
||||
@@ -530,6 +530,24 @@ class PaymentStep(QuestionsViewMixin, CartMixin, TemplateFlowStep):
|
||||
|
||||
def post(self, request):
|
||||
self.request = request
|
||||
if request.POST.get("giftcard"):
|
||||
# TODO: cross-organizer acceptance, check for valid money, …
|
||||
try:
|
||||
gc = GiftCard.objects.get(
|
||||
issuer=request.organizer,
|
||||
secret=request.POST.get("giftcard")
|
||||
)
|
||||
if gc.currency != request.event.currency:
|
||||
messages.error(self.request, _("This gift card does not support this currency."))
|
||||
return self.render()
|
||||
if 'gift_cards' not in self.cart_session:
|
||||
self.cart_session['gift_cards'] = []
|
||||
self.cart_session['gift_cards'] = self.cart_session['gift_cards'] + [gc.pk]
|
||||
return self.render()
|
||||
except GiftCard.DoesNotExist:
|
||||
messages.error(self.request, _("This gift card is not known."))
|
||||
return self.render()
|
||||
|
||||
for p in self.provider_forms:
|
||||
if p['provider'].identifier == request.POST.get('payment', ''):
|
||||
self.cart_session['payment'] = p['provider'].identifier
|
||||
@@ -709,7 +727,7 @@ class ConfirmStep(CartMixin, AsyncAction, TemplateFlowStep):
|
||||
return self.do(self.request.event.id, self.payment_provider.identifier if self.payment_provider else None,
|
||||
[p.id for p in self.positions], self.cart_session.get('email'),
|
||||
translation.get_language(), self.invoice_address.pk, meta_info,
|
||||
request.sales_channel)
|
||||
request.sales_channel, self.cart_session.get('gift_cards'))
|
||||
|
||||
def get_success_message(self, value):
|
||||
create_empty_cart_id(self.request)
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
<div class="panel-group" id="payment_accordion">
|
||||
{# TODO: make this proper #}
|
||||
<input type="text" class="form-control" placeholder="Add gift card" name="giftcard">
|
||||
{% for p in providers %}
|
||||
<div class="panel panel-default" data-total="{{ p.total|floatformat:2 }}">
|
||||
<label class="accordion-radio">
|
||||
|
||||
Reference in New Issue
Block a user