update tickets app with list and detail views
Also this commit allows users to download their ticket as PDF.
This commit is contained in:
parent
410905f3ce
commit
984b82ba11
|
@ -1,4 +1,3 @@
|
|||
from django.conf.urls.static import static
|
||||
from allauth.account.views import (
|
||||
LoginView,
|
||||
LogoutView,
|
||||
|
@ -13,12 +12,17 @@ from program.views import *
|
|||
from sponsors.views import *
|
||||
from teams.views import *
|
||||
from people.views import *
|
||||
from tickets.views import ShopTicketListView
|
||||
|
||||
urlpatterns = [
|
||||
url(
|
||||
r'^profile/',
|
||||
include('profiles.urls', namespace='profiles')
|
||||
),
|
||||
url(
|
||||
r'^tickets/',
|
||||
include('tickets.urls', namespace='tickets')
|
||||
),
|
||||
url(
|
||||
r'^shop/',
|
||||
include('shop.urls', namespace='shop')
|
||||
|
|
|
@ -11,11 +11,9 @@
|
|||
<a href="{% url 'shop:order_list' %}" class="btn btn-black">
|
||||
Orders
|
||||
</a>
|
||||
{% if has_tickets %}
|
||||
<a href="{% url 'shop:ticket_list' %}" class="btn btn-black">
|
||||
<a href="{% url 'tickets:shopticket_list' %}" class="btn btn-black">
|
||||
Tickets
|
||||
</a>
|
||||
{% endif %}
|
||||
{% if user.creditnotes.exists %}
|
||||
<a href="{% url 'shop:creditnote_list' %}" class="btn btn-black">
|
||||
Credit Notes
|
||||
|
|
|
@ -13,6 +13,7 @@ from django.utils.text import slugify
|
|||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils import timezone
|
||||
from django.core.urlresolvers import reverse_lazy
|
||||
from django.core.exceptions import ValidationError
|
||||
from decimal import Decimal
|
||||
from datetime import timedelta
|
||||
from unidecode import unidecode
|
||||
|
@ -312,6 +313,12 @@ class Product(CreatedUpdatedModel, UUIDModel):
|
|||
self.price,
|
||||
)
|
||||
|
||||
def clean(self):
|
||||
if self.category.name == 'Tickets' and not self.ticket_type:
|
||||
raise ValidationError(
|
||||
'Products with category Tickets need a ticket_type'
|
||||
)
|
||||
|
||||
def is_available(self):
|
||||
now = timezone.now()
|
||||
return now in self.available_in
|
||||
|
@ -324,7 +331,7 @@ class Product(CreatedUpdatedModel, UUIDModel):
|
|||
|
||||
def is_upcoming(self):
|
||||
now = timezone.now()
|
||||
return self.available_in.lower > now
|
||||
return self.available_in.lower > now
|
||||
|
||||
|
||||
class OrderProductRelation(CreatedUpdatedModel):
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
<li class="pull-right"><a href="{% url 'shop:creditnote_list' %}">Credit Notes</a></li>
|
||||
{% endif %}
|
||||
{% if has_tickets %}
|
||||
<li class="pull-right"><a href="{% url 'shop:ticket_list' %}">Tickets</a></li>
|
||||
<li class="pull-right"><a href="{% url 'tickets:shopticket_list' %}">Tickets</a></li>
|
||||
{% endif %}
|
||||
<li class="pull-right"><a href="{% url 'shop:order_list' %}">Orders</a></li>
|
||||
<li class="pull-right no-before">
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
{% extends 'shop_base.html' %}
|
||||
{% load bootstrap3 %}
|
||||
{% load shop_tags %}
|
||||
|
||||
{% block shop_content %}
|
||||
<h2>Under maintenance</h2>
|
||||
{% endblock %}
|
|
@ -1,5 +1,6 @@
|
|||
from django.conf.urls import url
|
||||
from .views import *
|
||||
from tickets.views import ShopTicketListView
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^$', ShopIndexView.as_view(), name='index'),
|
||||
|
@ -23,9 +24,6 @@ urlpatterns = [
|
|||
|
||||
url(r'orders/(?P<pk>[0-9]+)/pay/cash/$', CashView.as_view(), name='cash'),
|
||||
|
||||
url(r'tickets/$', TicketListView.as_view(), name='ticket_list'),
|
||||
url(r'tickets/(?P<pk>\b[0-9A-Fa-f]{8}\b(-\b[0-9A-Fa-f]{4}\b){3}-\b[0-9A-Fa-f]{12}\b)$', TicketDetailView.as_view(), name='ticket_detail'),
|
||||
|
||||
url(r'creditnotes/$', CreditNoteListView.as_view(), name='creditnote_list'),
|
||||
url(r'creditnotes/(?P<pk>[0-9]+)/pdf/$', DownloadCreditNoteView.as_view(), name='download_creditnote'),
|
||||
]
|
||||
|
|
|
@ -375,34 +375,6 @@ class DownloadCreditNoteView(LoginRequiredMixin, EnsureUserOwnsCreditNoteMixin,
|
|||
return response
|
||||
|
||||
|
||||
class TicketListView(LoginRequiredMixin, ListView):
|
||||
model = Ticket
|
||||
template_name = 'ticket_list.html'
|
||||
context_object_name = 'tickets'
|
||||
|
||||
def get_queryset(self):
|
||||
tickets = super(TicketListView, self).get_queryset()
|
||||
user = self.request.user
|
||||
return tickets.filter(order__user=user)
|
||||
|
||||
|
||||
class TicketDetailView(LoginRequiredMixin, UpdateView, DetailView):
|
||||
model = Ticket
|
||||
template_name = 'ticket_detail.html'
|
||||
context_object_name = 'ticket'
|
||||
fields = ['name', 'email']
|
||||
|
||||
def form_valid(self, form):
|
||||
messages.info(self.request, 'Ticket updated!')
|
||||
return super(TicketDetailView, self).form_valid(form)
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
ticket = self.get_object()
|
||||
if ticket.order.user != request.user:
|
||||
raise Http404
|
||||
return super(TicketDetailView, self).dispatch(request, *args, **kwargs)
|
||||
|
||||
|
||||
class OrderMarkAsPaidView(LoginRequiredMixin, SingleObjectMixin, View):
|
||||
|
||||
model = Order
|
||||
|
|
|
@ -8,7 +8,6 @@ from django.db import models
|
|||
from django.conf import settings
|
||||
from django.core.urlresolvers import reverse_lazy
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from utils.models import (
|
||||
UUIDModel,
|
||||
CreatedUpdatedModel
|
||||
|
@ -60,7 +59,7 @@ class BaseTicket(CreatedUpdatedModel, UUIDModel):
|
|||
return 'data:image/png;base64,{}'.format(self.qrcode_base64)
|
||||
|
||||
def generate_pdf(self):
|
||||
generate_pdf_letter(
|
||||
return generate_pdf_letter(
|
||||
filename='ticket_{}.pdf'.format(self.pk),
|
||||
formatdict={'ticket': self},
|
||||
template='pdf/ticket.html'
|
||||
|
@ -124,5 +123,6 @@ class ShopTicket(BaseTicket):
|
|||
super(ShopTicket, self).save(**kwargs)
|
||||
|
||||
def get_absolute_url(self):
|
||||
return str(reverse_lazy('shop:ticket_detail', kwargs={'pk': self.pk}))
|
||||
|
||||
return str(
|
||||
reverse_lazy('tickets:shopticket_edit', kwargs={'pk': self.pk})
|
||||
)
|
||||
|
|
|
@ -7,14 +7,9 @@
|
|||
<div class="well">
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-5" style="text-align: center;">
|
||||
<h3>{{ ticket.product.name }}</h3>
|
||||
<div style="margin:0px auto;width:250px;height:250px;background-image:url({{ ticket.get_qr_code_url }});"></div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-7">
|
||||
<div class="pull-right">
|
||||
<h2>{% if ticket.checked_in %}This ticket has been used{% else %}This ticket is unused{% endif %}</h2>
|
||||
<h2>{% if ticket.checked_in %}This ticket has been used{% else %}This ticket is unused{% endif %}</h2>
|
||||
<form method="POST" class="form">
|
||||
{% csrf_token %}
|
||||
{% bootstrap_field form.name %}
|
55
src/tickets/templates/ticket_list.html
Normal file
55
src/tickets/templates/ticket_list.html
Normal file
|
@ -0,0 +1,55 @@
|
|||
{% extends 'tickets_base.html' %}
|
||||
{% load bootstrap3 %}
|
||||
{% load tickets_tags %}
|
||||
|
||||
{% block tickets_content %}
|
||||
{% if tickets %}
|
||||
<h3>Tickets</h3>
|
||||
|
||||
<table class="table table-bordered table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
Ticket owner
|
||||
<th>
|
||||
Product name
|
||||
<th>
|
||||
Price
|
||||
<th>
|
||||
Checked in
|
||||
<th>
|
||||
Actions
|
||||
|
||||
<tbody>
|
||||
{% for ticket in tickets %}
|
||||
<tr>
|
||||
<td>
|
||||
{% if ticket.name %}
|
||||
{{ ticket.name }}
|
||||
{% else %}
|
||||
Anonymous
|
||||
{% endif %}
|
||||
<td>
|
||||
{{ ticket.product.name }}
|
||||
<td>
|
||||
{{ ticket.product.price|currency }}
|
||||
<td>
|
||||
{% if ticket.checked_in %}
|
||||
Yes
|
||||
{% else %}
|
||||
Not yet
|
||||
{% endif %}
|
||||
<td>
|
||||
<a href="{% url 'tickets:shopticket_download' pk=ticket.pk %}" class="btn btn-primary"><i class="fa fa-download" aria-hidden="true"></i> Download PDF</a>
|
||||
{% if not ticket.name %}
|
||||
<a href="{% url 'tickets:shopticket_edit' pk=ticket.pk %}" class="btn btn-primary"><i class="fa fa-edit" aria-hidden="true"></i> Set name</a>
|
||||
{% else %}
|
||||
<a href="{% url 'tickets:shopticket_edit' pk=ticket.pk %}" class="btn btn-primary"><i class="fa fa-edit" aria-hidden="true"></i> Edit name</a>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</table>
|
||||
{% else %}
|
||||
<h3> You dont have any tickets yet. We hope to see you at the next BornHack!</h3>
|
||||
{% endif %}
|
||||
|
||||
{% endblock %}
|
21
src/tickets/templates/tickets_base.html
Normal file
21
src/tickets/templates/tickets_base.html
Normal file
|
@ -0,0 +1,21 @@
|
|||
{% extends 'base.html' %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<ol class="breadcrumb">
|
||||
<li><a href="{% url 'tickets:shopticket_list' %}">Tickets</a></li>
|
||||
<li class="pull-right no-before">
|
||||
|
||||
</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% block tickets_content %}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% endblock %}
|
||||
|
12
src/tickets/templatetags/tickets_tags.py
Normal file
12
src/tickets/templatetags/tickets_tags.py
Normal file
|
@ -0,0 +1,12 @@
|
|||
from django import template
|
||||
from decimal import Decimal
|
||||
|
||||
register = template.Library()
|
||||
|
||||
|
||||
@register.filter
|
||||
def currency(value):
|
||||
try:
|
||||
return "{0:.2f} DKK".format(Decimal(value))
|
||||
except ValueError:
|
||||
return False
|
25
src/tickets/urls.py
Normal file
25
src/tickets/urls.py
Normal file
|
@ -0,0 +1,25 @@
|
|||
from django.conf.urls import url
|
||||
|
||||
from .views import (
|
||||
ShopTicketListView,
|
||||
ShopTicketDownloadView,
|
||||
ShopTicketDetailView
|
||||
)
|
||||
|
||||
urlpatterns = [
|
||||
url(
|
||||
r'tickets/$',
|
||||
ShopTicketListView.as_view(),
|
||||
name='shopticket_list'
|
||||
),
|
||||
url(
|
||||
r'tickets/(?P<pk>\b[0-9A-Fa-f]{8}\b(-\b[0-9A-Fa-f]{4}\b){3}-\b[0-9A-Fa-f]{12}\b)/download$',
|
||||
ShopTicketDownloadView.as_view(),
|
||||
name='shopticket_download'
|
||||
),
|
||||
url(
|
||||
r'tickets/(?P<pk>\b[0-9A-Fa-f]{8}\b(-\b[0-9A-Fa-f]{4}\b){3}-\b[0-9A-Fa-f]{12}\b)/edit$',
|
||||
ShopTicketDetailView.as_view(),
|
||||
name='shopticket_edit'
|
||||
),
|
||||
]
|
|
@ -1,3 +1,64 @@
|
|||
from django.shortcuts import render
|
||||
import logging
|
||||
|
||||
# Create your views here.
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.views.generic.detail import SingleObjectMixin
|
||||
from django.views.generic import (
|
||||
DetailView,
|
||||
UpdateView,
|
||||
ListView,
|
||||
View
|
||||
)
|
||||
from django.http import (
|
||||
HttpResponse,
|
||||
Http404
|
||||
)
|
||||
|
||||
from .models import ShopTicket
|
||||
logger = logging.getLogger("bornhack.%s" % __name__)
|
||||
|
||||
|
||||
class ShopTicketListView(LoginRequiredMixin, ListView):
|
||||
model = ShopTicket
|
||||
template_name = 'ticket_list.html'
|
||||
context_object_name = 'tickets'
|
||||
|
||||
def get_queryset(self):
|
||||
tickets = super(ShopTicketListView, self).get_queryset()
|
||||
user = self.request.user
|
||||
return tickets.filter(order__user=user)
|
||||
|
||||
|
||||
class ShopTicketDownloadView(LoginRequiredMixin, SingleObjectMixin, View):
|
||||
model = ShopTicket
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
if not request.user == self.get_object().order.user:
|
||||
raise Http404("Ticket not found")
|
||||
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
response = HttpResponse(content_type='application/pdf')
|
||||
response['Content-Disposition'] = 'attachment; filename="ticket_{pk}.pdf"'.format(
|
||||
pk=self.get_object().pk
|
||||
)
|
||||
response.write(self.get_object().generate_pdf().getvalue())
|
||||
return response
|
||||
|
||||
|
||||
class ShopTicketDetailView(LoginRequiredMixin, UpdateView, DetailView):
|
||||
model = ShopTicket
|
||||
template_name = 'ticket_detail.html'
|
||||
context_object_name = 'ticket'
|
||||
fields = ['name', 'email']
|
||||
|
||||
def form_valid(self, form):
|
||||
messages.info(self.request, 'Ticket updated!')
|
||||
return super().form_valid(form)
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
ticket = self.get_object()
|
||||
if ticket.order.user != request.user:
|
||||
raise Http404("Ticket not found")
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
|
|
|
@ -64,7 +64,6 @@ def generate_pdf_letter(filename, template, formatdict):
|
|||
finalpdf.write(fh)
|
||||
logger.info('Saved pdf to archive: %s' % fullpath)
|
||||
|
||||
# return a file object with the data
|
||||
returnfile = io.BytesIO()
|
||||
finalpdf.write(returnfile)
|
||||
return returnfile
|
||||
|
|
Loading…
Reference in a new issue