Display payment details

This commit is contained in:
Raphael Michel
2015-03-12 22:54:59 +01:00
parent 3e392f7ed4
commit 8c802e534e
11 changed files with 306 additions and 51 deletions

View File

@@ -25,6 +25,11 @@
}
}
}
.panel-body address:last-child {
margin-bottom: 0;
}
.cart-row, .product-row {
padding: 10px 0;
@@ -43,6 +48,15 @@
&.total {
border-top: 1px solid @table-border-color;
}
dl {
padding-left: 20px;
margin-bottom: 0;
dd {
padding-left: 20px;
}
}
}
.panel-primary .panel-heading a {
color: white;

View File

@@ -1,44 +1,57 @@
{% load i18n %}
{% for line in cart.positions %}
<div class="row-fluid cart-row">
<div class="col-md-4 col-xs-6">
<div class="{% if line.has_questions %}col-md-9 col-xs-12{% else %}col-md-4 col-xs-6{% endif %}">
<strong>{{ line.item }}</strong>
{% if line.variation %}
{{ line.variation }}
{% endif %}
</div>
<div class="col-md-3 col-xs-6 price">
{{ event.currency }} {{ line.price|floatformat:2 }}
</div>
<div class="col-md-2 col-xs-6 count">
{% if editable %}
<form action="{% url "presale:event.cart.remove" event=event.slug organizer=event.organizer.slug %}"
{% if line.has_questions %}
<dl>
{% if line.item.admission and event.settings.attendee_names_asked == 'True' %}
<dt>{% trans "Attendee name" %}</dt>
<dd>{% if line.attendee_name %}{{ line.attendee_name }}{% else %}<em>{% trans "not answered" %}</em>{% endif %}</dd>
{% endif %}
{% for q in line.questions %}
<dt>{{ q.question }}</dt>
<dd>{% if q.answer %}{{ q.answer }}{% else %}<em>{% trans "not answered" %}</em>{% endif %}</dd>
{% endfor %}
</dl>
{% else %}
</div>
<div class="col-md-3 col-xs-6 price">
{{ event.currency }} {{ line.price|floatformat:2 }}
</div>
<div class="col-md-2 col-xs-6 count">
{% if editable %}
<form action="{% url "presale:event.cart.remove" event=event.slug organizer=event.organizer.slug %}"
method="post">
{% csrf_token %}
{% if line.variation %}
<input type="hidden" name="variation_{{ line.item.identity }}_{{ line.variation.identity }}"
value="1" />
{% else %}
<input type="hidden" name="item_{{ line.item.identity }}"
value="1" />
{% endif %}
<button class="btn btn-mini btn-link"><i class="fa fa-minus"></i></button>
</form>
{% endif %}
{{ line.count }}
{% if editable %}
<form action="{% url "presale:event.cart.add" event=event.slug organizer=event.organizer.slug %}"
method="post">
{% csrf_token %}
{% if line.variation %}
<input type="hidden" name="variation_{{ line.item.identity }}_{{ line.variation.identity }}"
value="1" />
{% else %}
<input type="hidden" name="item_{{ line.item.identity }}"
{% csrf_token %}
{% if line.variation %}
<input type="hidden" name="variation_{{ line.item.identity }}_{{ line.variation.identity }}"
value="1" />
{% else %}
<input type="hidden" name="item_{{ line.item.identity }}"
value="1" />
{% endif %}
<button class="btn btn-mini btn-link"><i class="fa fa-plus"></i></button>
</form>
{% endif %}
<button class="btn btn-mini btn-link"><i class="fa fa-minus"></i></button>
</form>
{% endif %}
{{ line.count }}
{% if editable %}
<form action="{% url "presale:event.cart.add" event=event.slug organizer=event.organizer.slug %}"
method="post">
{% csrf_token %}
{% if line.variation %}
<input type="hidden" name="variation_{{ line.item.identity }}_{{ line.variation.identity }}"
value="1" />
{% else %}
<input type="hidden" name="item_{{ line.item.identity }}"
value="1" />
{% endif %}
<button class="btn btn-mini btn-link"><i class="fa fa-plus"></i></button>
</form>
{% endif %}
</div>
<div class="col-md-3 col-xs-6 price">

View File

@@ -0,0 +1,69 @@
{% extends "pretixpresale/event/base.html" %}
{% load i18n %}
{% load bootstrap3 %}
{% block title %}{% trans "Order details" %}{% endblock %}
{% block content %}
<h2>
{% blocktrans trimmed with code=order.code %}
Your order: {{ code }}
{% endblocktrans %}
{% if order.status == "n" %}
<span class="label label-warning">{% trans "Payment pending" %}</span>
{% elif order.status == "p" %}
<span class="label label-success">{% trans "Paid" %}</span>
{% elif order.status == "e" %}
<span class="label label-danger">{% trans "Payment pending" %}</span>
{% elif order.status == "c" %}
<span class="label label-danger">{% trans "Cancelled" %}</span>
{% elif order.status == "r" %}
<span class="label label-danger">{% trans "Refunded" %}</span>
{% endif %}
</h2>
{% if order.status == "n" %}
<div class="panel panel-danger">
<div class="panel-heading">
<h3 class="panel-title">
{% trans "Payment" %}
</h3>
</div>
<div class="panel-body">
{{ payment }}
<strong>{% blocktrans trimmed with date=order.expires|date %}
Please complete your payment before {{ date }}
{% endblocktrans %}</strong>
</div>
</div>
{% endif %}
<div class="panel panel-primary cart">
<div class="panel-heading">
<h3 class="panel-title">
{% trans "Ordered items" %}
</h3>
</div>
<div class="panel-body">
{% include "pretixpresale/event/fragment_cart.html" with cart=cart event=request.event editable=False %}
</div>
</div>
{% if order.status == "n" %}
<div class="row">
<div class="col-md-12 text-right">
<form action="" method="post">
{% csrf_token %}
<button type="submit" class="btn btn-danger">{% trans "Cancel order" %}</button>
</form>
</div>
</div>
{% endif %}
{% if order.status == "p" and payment %}
<div class="panel panel-success">
<div class="panel-heading">
<h3 class="panel-title">
{% trans "Payment" %}
</h3>
</div>
<div class="panel-body">
{{ payment }}
</div>
</div>
{% endif %}
{% endblock %}

View File

@@ -3,6 +3,7 @@ from django.conf.urls import patterns, url, include
import pretix.presale.views.event
import pretix.presale.views.cart
import pretix.presale.views.checkout
import pretix.presale.views.order
urlpatterns = patterns(
@@ -18,7 +19,7 @@ urlpatterns = patterns(
name='event.checkout.payment'),
url(r'^checkout/confirm$', pretix.presale.views.checkout.OrderConfirm.as_view(),
name='event.checkout.confirm'),
url(r'^order/(?P<order>[^/]+)/$', pretix.presale.views.checkout.OrderConfirm.as_view(),
url(r'^order/(?P<order>[^/]+)/$', pretix.presale.views.order.OrderDetails.as_view(),
name='event.order'),
url(r'^login$', pretix.presale.views.event.EventLogin.as_view(), name='event.checkout.login'),
)

View File

@@ -52,22 +52,33 @@ class CartDisplayMixin:
'item__questions', 'answers'
))
def get_cart(self):
cartpos = CartPosition.objects.current.filter(
Q(user=self.request.user) & Q(event=self.request.event)
).order_by(
def get_cart(self, answers=False, queryset=None, payment_fee=None):
if queryset is None:
queryset = CartPosition.objects.current.filter(
Q(user=self.request.user) & Q(event=self.request.event)
)
prefetch = ['variation__values', 'variation__values__prop']
if answers:
prefetch.append('item__questions')
prefetch.append('answers')
cartpos = queryset.order_by(
'item', 'variation'
).select_related(
'item', 'variation'
).prefetch_related(
'variation__values', 'variation__values__prop'
*prefetch
)
# Group items of the same variation
# We do this by list manipulations instead of a GROUP BY query, as
# Django is unable to join related models in a .values() query
def keyfunc(pos):
return pos.item_id, pos.variation_id, pos.price
if answers and ((pos.item.admission and self.request.event.settings.attendee_names_asked == 'True')
or pos.item.questions.all()):
return pos.id, "", "", ""
return "", pos.item_id, pos.variation_id, pos.price
positions = []
for k, g in groupby(sorted(list(cartpos), key=keyfunc), key=keyfunc):
@@ -75,27 +86,42 @@ class CartDisplayMixin:
group = g[0]
group.count = len(g)
group.total = group.count * group.price
group.has_questions = answers and k[0] != ""
if answers:
group.answ = {}
for a in group.answers.all():
group.answ[a.question_id] = a.answer
group.questions = []
for q in group.item.questions.all():
if q.identity in group.answ:
q.answer = group.answ[q.identity]
else:
q.answer = ""
group.questions.append(q)
positions.append(group)
total = sum(p.total for p in positions)
payment_fee = 0
if 'payment' in self.request.session:
responses = register_payment_providers.send(self.request.event)
for receiver, response in responses:
provider = response(self.request.event)
if provider.identifier == self.request.session['payment']:
payment_fee = provider.calculate_fee(total)
if payment_fee is None:
payment_fee = 0
if 'payment' in self.request.session:
responses = register_payment_providers.send(self.request.event)
for receiver, response in responses:
provider = response(self.request.event)
if provider.identifier == self.request.session['payment']:
payment_fee = provider.calculate_fee(total)
try:
minutes_left = max(min(p.expires for p in positions) - now(), timedelta()).seconds // 60 if positions else 0
except AttributeError:
minutes_left = None
return {
'positions': positions,
'raw': cartpos,
'total': total + payment_fee,
'payment_fee': payment_fee,
'minutes_left': (
max(min(p.expires for p in positions) - now(), timedelta()).seconds // 60
if positions else 0
),
'answers': answers,
'minutes_left': minutes_left,
}

View File

@@ -251,7 +251,7 @@ class OrderConfirm(EventViewMixin, CartDisplayMixin, EventLoginRequiredMixin, Ch
def get_context_data(self, **kwargs):
ctx = super().get_context_data(**kwargs)
ctx['cart'] = self.get_cart()
ctx['cart'] = self.get_cart(answers=True)
ctx['payment'] = self.payment_provider.checkout_confirm_render(self.request)
return ctx

View File

@@ -0,0 +1,90 @@
from itertools import groupby
from django.db.models import Q
from django.utils.functional import cached_property
from django.views.generic import TemplateView
from django.http import HttpResponseNotFound
from pretix.base.models import Order, OrderPosition
from pretix.base.signals import register_payment_providers
from pretix.presale.views import EventViewMixin, EventLoginRequiredMixin, CartDisplayMixin
class OrderDetails(EventViewMixin, EventLoginRequiredMixin, CartDisplayMixin, TemplateView):
template_name = "pretixpresale/event/order.html"
@cached_property
def order(self):
try:
return Order.objects.current.get(
user=self.request.user,
event=self.request.event,
code=self.kwargs['order'],
)
except Order.DoesNotExist:
return None
def get(self, request, *args, **kwargs):
self.kwargs = kwargs
if not self.order:
return HttpResponseNotFound
return super().get(request, *args, **kwargs)
def itemlist_cartlike(self):
"""
Returns the list of ordered items a format compatible to the
CardDisplayMixin, so we can reuse template code
"""
cartpos = OrderPosition.objects.current.filter(
order=self.order,
).order_by(
'item', 'variation'
).select_related(
'item', 'variation'
).prefetch_related(
'variation__values', 'variation__values__prop',
'item__questions'
)
# Group items of the same variation
# We do this by list manipulations instead of a GROUP BY query, as
# Django is unable to join related models in a .values() query
def keyfunc(pos):
if (pos.item.admission and self.request.event.settings.attendee_names_asked == 'True') \
or pos.item.questions.all():
return pos.id, "", "", ""
return "", pos.item_id, pos.variation_id, pos.price
positions = []
for k, g in groupby(sorted(list(cartpos), key=keyfunc), key=keyfunc):
g = list(g)
group = g[0]
group.count = len(g)
group.total = group.count * group.price
positions.append(group)
return {
'positions': positions,
'raw': cartpos,
'total': self.order.total,
'payment_fee': self.order.payment_fee,
}
@cached_property
def payment_provider(self):
responses = register_payment_providers.send(self.request.event)
for receiver, response in responses:
provider = response(self.request.event)
if provider.identifier == self.order.payment_provider:
return provider
def get_context_data(self, **kwargs):
ctx = super().get_context_data(**kwargs)
ctx['order'] = self.order
ctx['cart'] = self.get_cart(
answers=True,
queryset=OrderPosition.objects.current.filter(order=self.order)
)
if self.order.status == Order.STATUS_PENDING:
ctx['payment'] = self.payment_provider.order_pending_render(self.request, self.order)
elif self.order.status == Order.STATUS_PAID:
ctx['payment'] = self.payment_provider.order_paid_render(self.request, self.order)
return ctx