List and display tickets (close #6)
This commit is contained in:
parent
093f9bddf9
commit
aa3a3c0428
|
@ -59,6 +59,7 @@ TEMPLATES = [
|
||||||
'django.contrib.messages.context_processors.messages',
|
'django.contrib.messages.context_processors.messages',
|
||||||
'camps.context_processors.current_camp',
|
'camps.context_processors.current_camp',
|
||||||
'shop.context_processors.current_order',
|
'shop.context_processors.current_order',
|
||||||
|
'shop.context_processors.user_has_tickets',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -106,6 +106,6 @@ footer {
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.breadcrumb > li.pull-right::before {
|
.breadcrumb > li.no-before::before {
|
||||||
content: "";
|
content: "";
|
||||||
}
|
}
|
||||||
|
|
19
news/migrations/0002_auto_20160530_2223.py
Normal file
19
news/migrations/0002_auto_20160530_2223.py
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.9.6 on 2016-05-30 22:23
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('news', '0001_initial'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='newsitem',
|
||||||
|
options={'ordering': ['-published_at']},
|
||||||
|
),
|
||||||
|
]
|
|
@ -1,3 +1,6 @@
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
|
|
||||||
def current_order(request):
|
def current_order(request):
|
||||||
if request.user.is_authenticated():
|
if request.user.is_authenticated():
|
||||||
order = None
|
order = None
|
||||||
|
@ -10,3 +13,10 @@ def current_order(request):
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
|
||||||
|
def user_has_tickets(request):
|
||||||
|
has_tickets = False
|
||||||
|
if request.user.orders.filter(
|
||||||
|
tickets__product__category__pk=settings.TICKET_CATEGORY_ID
|
||||||
|
).exists():
|
||||||
|
has_tickets = True
|
||||||
|
return {'has_tickets': has_tickets}
|
||||||
|
|
20
shop/migrations/0021_ticket_email.py
Normal file
20
shop/migrations/0021_ticket_email.py
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.9.6 on 2016-05-30 22:23
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('shop', '0020_auto_20160530_1824'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='ticket',
|
||||||
|
name='email',
|
||||||
|
field=models.EmailField(blank=True, max_length=254, null=True),
|
||||||
|
),
|
||||||
|
]
|
|
@ -288,8 +288,8 @@ class CoinifyAPICallback(CreatedUpdatedModel):
|
||||||
|
|
||||||
|
|
||||||
class Ticket(CreatedUpdatedModel, UUIDModel):
|
class Ticket(CreatedUpdatedModel, UUIDModel):
|
||||||
order = models.ForeignKey('shop.Order')
|
order = models.ForeignKey('shop.Order', related_name='tickets')
|
||||||
product = models.ForeignKey('shop.Product')
|
product = models.ForeignKey('shop.Product', related_name='tickets')
|
||||||
qrcode_base64 = models.TextField(null=True, blank=True)
|
qrcode_base64 = models.TextField(null=True, blank=True)
|
||||||
|
|
||||||
name = models.CharField(
|
name = models.CharField(
|
||||||
|
@ -300,6 +300,11 @@ class Ticket(CreatedUpdatedModel, UUIDModel):
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
email = models.EmailField(
|
||||||
|
null=True,
|
||||||
|
blank=True,
|
||||||
|
)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return 'Ticket {user} {product}'.format(
|
return 'Ticket {user} {product}'.format(
|
||||||
user=self.order.user,
|
user=self.order.user,
|
||||||
|
@ -321,12 +326,18 @@ class Ticket(CreatedUpdatedModel, UUIDModel):
|
||||||
).hexdigest()
|
).hexdigest()
|
||||||
|
|
||||||
def get_qr_code(self):
|
def get_qr_code(self):
|
||||||
qr = qrcode.make(self.get_token())
|
qr = qrcode.make(
|
||||||
|
self.get_token(),
|
||||||
|
version=1,
|
||||||
|
error_correction=qrcode.constants.ERROR_CORRECT_H
|
||||||
|
).resize((250,250))
|
||||||
file_like = io.BytesIO()
|
file_like = io.BytesIO()
|
||||||
qr.save(file_like)
|
qr.save(file_like, format='png')
|
||||||
qrcode_base64 = base64.b64encode(file_like.getvalue())
|
qrcode_base64 = base64.b64encode(file_like.getvalue())
|
||||||
return qrcode_base64
|
return qrcode_base64
|
||||||
|
|
||||||
def get_qr_code_url(self):
|
def get_qr_code_url(self):
|
||||||
return 'data:image/png;base64,{}'.format(self.qrcode_base64)
|
return 'data:image/png;base64,{}'.format(self.qrcode_base64)
|
||||||
|
|
||||||
|
def get_absolute_url(self):
|
||||||
|
return str(reverse_lazy('shop:ticket_detail', kwargs={'pk': self.pk}))
|
||||||
|
|
|
@ -6,12 +6,12 @@
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
|
||||||
<div class="col-xs-12 col-md-8">
|
<div class="col-sm-12 col-md-8">
|
||||||
<h2>{{ product.name }}</h2>
|
<h2>{{ product.name }}</h2>
|
||||||
{{ product.description|commonmark }}
|
{{ product.description|commonmark }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-xs-12 col-md-4">
|
<div class="col-sm-12 col-md-4">
|
||||||
|
|
||||||
<h3>Add to order</h3>
|
<h3>Add to order</h3>
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,10 @@
|
||||||
<li class="active">{{ current_category }}</li>
|
<li class="active">{{ current_category }}</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if user.is_authenticated and user.orders.exists %}
|
{% if user.is_authenticated and user.orders.exists %}
|
||||||
<li class="pull-right"><a href="{% url 'shop:order_list' %}">Previous orders</a></li>
|
{% if has_tickets %}
|
||||||
|
<li class="pull-right"><a href="{% url 'shop:ticket_list' %}">Tickets</a></li>
|
||||||
|
{% endif %}
|
||||||
|
<li class="pull-right no-before"><a href="{% url 'shop:order_list' %}">Orders</a></li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</ol>
|
</ol>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
{% extends 'shop_base.html' %}
|
||||||
|
{% load bootstrap3 %}
|
||||||
|
{% load shop_tags %}
|
||||||
|
|
||||||
|
{% block shop_content %}
|
||||||
|
|
||||||
|
<div class="well">
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-5" style="text-align: center;">
|
||||||
|
<h3>{{ ticket.product.name }} ticket</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>{{ ticket.order.camp }}</h2>
|
||||||
|
<form method="POST" class="form">
|
||||||
|
{% csrf_token %}
|
||||||
|
{% bootstrap_field form.name %}
|
||||||
|
{% bootstrap_field form.email %}
|
||||||
|
<button class="btn btn-primary form-control" type="submit"><i class="glyphicon glyphicon-check"></i> Save</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<hr />
|
||||||
|
|
||||||
|
<div class="pull-right">
|
||||||
|
<a href="#" class="btn btn-default">Answer questionnaire</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
|
@ -3,9 +3,38 @@
|
||||||
{% load shop_tags %}
|
{% load shop_tags %}
|
||||||
|
|
||||||
{% block shop_content %}
|
{% block shop_content %}
|
||||||
|
<h3>Tickets</h3>
|
||||||
|
|
||||||
|
<table class="table table-bordered table-hover">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>
|
||||||
|
Ticket owner
|
||||||
|
<th>
|
||||||
|
Product name
|
||||||
|
<th>
|
||||||
|
Price
|
||||||
|
|
||||||
|
<tbody>
|
||||||
{% for ticket in tickets %}
|
{% for ticket in tickets %}
|
||||||
{{ ticket }}
|
<tr>
|
||||||
|
<td>
|
||||||
|
<a href="{% url 'shop:ticket_detail' pk=ticket.pk %}" class="btn">
|
||||||
|
{% if ticket.name %}
|
||||||
|
{{ ticket.name }}
|
||||||
|
{% else %}
|
||||||
|
Click here to set the owner of this ticket
|
||||||
|
{% endif %}
|
||||||
|
</a>
|
||||||
|
<td>
|
||||||
|
<a href="{% url 'shop:ticket_detail' pk=ticket.pk %}" class="btn">
|
||||||
|
{{ ticket.product.name }}
|
||||||
|
<i class="glyphicon glyphicon-eye"></i>
|
||||||
|
</a>
|
||||||
|
<td>
|
||||||
|
{{ ticket.product.price|currency }}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -21,6 +21,7 @@ urlpatterns = [
|
||||||
url(r'orders/(?P<pk>[0-9]+)/pay/banktransfer/$', BankTransferView.as_view(), name='bank_transfer'),
|
url(r'orders/(?P<pk>[0-9]+)/pay/banktransfer/$', BankTransferView.as_view(), name='bank_transfer'),
|
||||||
|
|
||||||
url(r'tickets/$', TicketListView.as_view(), name='ticket_list'),
|
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'privacy-policy/$', TemplateView.as_view(template_name='law/privacy_policy.html'), name='privacy-policy'),
|
url(r'privacy-policy/$', TemplateView.as_view(template_name='law/privacy_policy.html'), name='privacy-policy'),
|
||||||
url(r'return-policy/$', TemplateView.as_view(template_name='law/return_policy.html'), name='return-policy'),
|
url(r'return-policy/$', TemplateView.as_view(template_name='law/return_policy.html'), name='return-policy'),
|
||||||
|
|
|
@ -11,6 +11,7 @@ from django.views.generic import (
|
||||||
ListView,
|
ListView,
|
||||||
DetailView,
|
DetailView,
|
||||||
FormView,
|
FormView,
|
||||||
|
UpdateView,
|
||||||
)
|
)
|
||||||
from django.views.generic.base import RedirectView
|
from django.views.generic.base import RedirectView
|
||||||
from django.views.generic.detail import SingleObjectMixin
|
from django.views.generic.detail import SingleObjectMixin
|
||||||
|
@ -486,3 +487,19 @@ class TicketListView(LoginRequiredMixin, ListView):
|
||||||
user = self.request.user
|
user = self.request.user
|
||||||
return tickets.filter(order__user=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:
|
||||||
|
return Http404
|
||||||
|
return super(TicketDetailView, self).dispatch(request, *args, **kwargs)
|
||||||
|
|
Loading…
Reference in a new issue