diff --git a/bornhack/settings/base.py b/bornhack/settings/base.py index f14b6e6c..f67d73a9 100644 --- a/bornhack/settings/base.py +++ b/bornhack/settings/base.py @@ -59,6 +59,7 @@ TEMPLATES = [ 'django.contrib.messages.context_processors.messages', 'camps.context_processors.current_camp', 'shop.context_processors.current_order', + 'shop.context_processors.user_has_tickets', ], }, }, diff --git a/bornhack/static_src/css/bornhack.css b/bornhack/static_src/css/bornhack.css index 09078f53..0af22276 100644 --- a/bornhack/static_src/css/bornhack.css +++ b/bornhack/static_src/css/bornhack.css @@ -106,6 +106,6 @@ footer { padding: 5px; } -.breadcrumb > li.pull-right::before { +.breadcrumb > li.no-before::before { content: ""; } diff --git a/news/migrations/0002_auto_20160530_2223.py b/news/migrations/0002_auto_20160530_2223.py new file mode 100644 index 00000000..63852d09 --- /dev/null +++ b/news/migrations/0002_auto_20160530_2223.py @@ -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']}, + ), + ] diff --git a/shop/context_processors.py b/shop/context_processors.py index c46c6bb2..8246d85f 100644 --- a/shop/context_processors.py +++ b/shop/context_processors.py @@ -1,3 +1,6 @@ +from django.conf import settings + + def current_order(request): if request.user.is_authenticated(): order = None @@ -10,3 +13,10 @@ def current_order(request): 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} diff --git a/shop/migrations/0021_ticket_email.py b/shop/migrations/0021_ticket_email.py new file mode 100644 index 00000000..8476aa42 --- /dev/null +++ b/shop/migrations/0021_ticket_email.py @@ -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), + ), + ] diff --git a/shop/models.py b/shop/models.py index 7ecda5e6..ef354241 100644 --- a/shop/models.py +++ b/shop/models.py @@ -288,8 +288,8 @@ class CoinifyAPICallback(CreatedUpdatedModel): class Ticket(CreatedUpdatedModel, UUIDModel): - order = models.ForeignKey('shop.Order') - product = models.ForeignKey('shop.Product') + order = models.ForeignKey('shop.Order', related_name='tickets') + product = models.ForeignKey('shop.Product', related_name='tickets') qrcode_base64 = models.TextField(null=True, blank=True) name = models.CharField( @@ -300,6 +300,11 @@ class Ticket(CreatedUpdatedModel, UUIDModel): ), ) + email = models.EmailField( + null=True, + blank=True, + ) + def __str__(self): return 'Ticket {user} {product}'.format( user=self.order.user, @@ -321,12 +326,18 @@ class Ticket(CreatedUpdatedModel, UUIDModel): ).hexdigest() 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() - qr.save(file_like) + qr.save(file_like, format='png') qrcode_base64 = base64.b64encode(file_like.getvalue()) return qrcode_base64 def get_qr_code_url(self): 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})) diff --git a/shop/templates/product_detail.html b/shop/templates/product_detail.html index 0888bbba..d2794126 100644 --- a/shop/templates/product_detail.html +++ b/shop/templates/product_detail.html @@ -6,12 +6,12 @@
-
+

{{ product.name }}

{{ product.description|commonmark }}
-
+

Add to order

diff --git a/shop/templates/shop_base.html b/shop/templates/shop_base.html index 1c846149..37cc969d 100644 --- a/shop/templates/shop_base.html +++ b/shop/templates/shop_base.html @@ -13,7 +13,10 @@
  • {{ current_category }}
  • {% endif %} {% if user.is_authenticated and user.orders.exists %} -
  • Previous orders
  • + {% if has_tickets %} +
  • Tickets
  • + {% endif %} +
  • Orders
  • {% endif %}
    diff --git a/shop/templates/ticket_detail.html b/shop/templates/ticket_detail.html index e69de29b..cacb2eb6 100644 --- a/shop/templates/ticket_detail.html +++ b/shop/templates/ticket_detail.html @@ -0,0 +1,39 @@ +{% extends 'shop_base.html' %} +{% load bootstrap3 %} +{% load shop_tags %} + +{% block shop_content %} + +
    + +
    +
    +

    {{ ticket.product.name }} ticket

    +
    +
    + +
    +
    +

    {{ ticket.order.camp }}

    +
    + {% csrf_token %} + {% bootstrap_field form.name %} + {% bootstrap_field form.email %} + +
    + +
    + + +
    +
    + + +
    + + +
    + +{% endblock %} diff --git a/shop/templates/ticket_list.html b/shop/templates/ticket_list.html index fd30a6ac..61541fe8 100644 --- a/shop/templates/ticket_list.html +++ b/shop/templates/ticket_list.html @@ -3,9 +3,38 @@ {% load shop_tags %} {% block shop_content %} +

    Tickets

    + + + + + + {% for ticket in tickets %} + +
    + Ticket owner + + Product name + + Price + +
    + + {% if ticket.name %} + {{ ticket.name }} + {% else %} + Click here to set the owner of this ticket + {% endif %} + + + + {{ ticket.product.name }} + + + + {{ ticket.product.price|currency }} + {% endfor %} +
    -{% for ticket in tickets %} - {{ ticket }} -{% endfor %} {% endblock %} diff --git a/shop/urls.py b/shop/urls.py index 83b5bf14..8afe30cb 100644 --- a/shop/urls.py +++ b/shop/urls.py @@ -21,6 +21,7 @@ urlpatterns = [ url(r'orders/(?P[0-9]+)/pay/banktransfer/$', BankTransferView.as_view(), name='bank_transfer'), url(r'tickets/$', TicketListView.as_view(), name='ticket_list'), + url(r'tickets/(?P\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'return-policy/$', TemplateView.as_view(template_name='law/return_policy.html'), name='return-policy'), diff --git a/shop/views.py b/shop/views.py index b680f88f..0f3e0c2a 100644 --- a/shop/views.py +++ b/shop/views.py @@ -11,6 +11,7 @@ from django.views.generic import ( ListView, DetailView, FormView, + UpdateView, ) from django.views.generic.base import RedirectView from django.views.generic.detail import SingleObjectMixin @@ -486,3 +487,19 @@ class TicketListView(LoginRequiredMixin, ListView): 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: + return Http404 + return super(TicketDetailView, self).dispatch(request, *args, **kwargs)