Merge remote-tracking branch 'origin/feature/tickets' into feature/tickets
# Conflicts: # shop/models.py # shop/urls.py
This commit is contained in:
commit
6016a6b605
|
@ -98,3 +98,5 @@ BOOTSTRAP3 = {
|
|||
|
||||
EPAY_MERCHANT_NUMBER = env('EPAY_MERCHANT_NUMBER')
|
||||
EPAY_MD5_SECRET = env('EPAY_MD5_SECRET')
|
||||
|
||||
TICKET_CATEGORY_ID = env('TICKET_CATEGORY_ID')
|
|
@ -9,3 +9,4 @@ EMAIL_USE_TLS=True
|
|||
EMAIL_FROM='noreply@example.com'
|
||||
EPAY_MERCHANT_NUMBER=something
|
||||
EPAY_MD5_SECRET=something
|
||||
TICKET_CATEGORY_ID=''
|
|
@ -86,3 +86,13 @@ a, a:active, a:focus {
|
|||
height: 500px;
|
||||
}
|
||||
|
||||
|
||||
/* Footer */
|
||||
footer {
|
||||
position: fixed;
|
||||
width: 700px;
|
||||
text-align: center;
|
||||
background-color: white;
|
||||
bottom: 0px;
|
||||
padding: 5px;
|
||||
}
|
||||
|
|
|
@ -53,6 +53,7 @@
|
|||
|
||||
{% endif %}
|
||||
<li><a href="{% url 'good-to-know' %}">Info</a></li>
|
||||
<li><a href="{% url 'contact' %}">Contact</a></li>
|
||||
{% if user.is_authenticated %}
|
||||
<li><a href="{% url 'profiles:detail' %}">Profile</a></li>
|
||||
<li><a href="{% url 'account_logout' %}">Logout</a></li>
|
||||
|
@ -69,6 +70,11 @@
|
|||
<div id="main" class="container container-fluid">
|
||||
{% bootstrap_messages %}
|
||||
{% block content %}{% endblock %}
|
||||
<footer class="row">
|
||||
<div class="col-sm-12 col-md-12 col-lg-12">
|
||||
{% block footer %}{% endblock %}
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
{% bootstrap_javascript jquery=1 %}
|
||||
</body>
|
||||
|
|
20
bornhack/templates/contact.html
Normal file
20
bornhack/templates/contact.html
Normal file
|
@ -0,0 +1,20 @@
|
|||
{% extends 'base.html' %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<p>
|
||||
The legal entity behind BornHack is:<br />
|
||||
BornHack IVS c/o Thomas Steen Rasmussen<br />
|
||||
Offenbachsvej 34, 2. tv.<br />
|
||||
2450 Copenhagen SV<br />
|
||||
Denmark<br />
|
||||
CVR# 37666521<br />
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Email us at info@bornhack.dk<br />
|
||||
Tweet us at @bornhax<br />
|
||||
Hit us up on IRC at #bornhack @ irc.baconsvin.org (TLS port 6697)<br />
|
||||
</p>
|
||||
|
||||
{% endblock %}
|
|
@ -21,6 +21,11 @@ urlpatterns = [
|
|||
TemplateView.as_view(template_name='good_to_know.html'),
|
||||
name='good-to-know'
|
||||
),
|
||||
url(
|
||||
r'contact/',
|
||||
TemplateView.as_view(template_name='contact.html'),
|
||||
name='contact'
|
||||
),
|
||||
url(
|
||||
r'^login/$',
|
||||
LoginView.as_view(),
|
||||
|
|
|
@ -5,3 +5,5 @@ django-environ>=0.4.0
|
|||
psycopg2>=2.6.1
|
||||
PyPDF2>=1.25.1
|
||||
django-wkhtmltopdf>=3.0.0
|
||||
Pillow==3.2.0
|
||||
qrcode==5.3
|
|
@ -1,18 +1,19 @@
|
|||
from django.contrib import admin
|
||||
|
||||
from .models import Order, ProductCategory, Product, OrderProductRelation, EpayCallback, EpayPayment
|
||||
from . import models
|
||||
|
||||
admin.site.register(EpayCallback)
|
||||
admin.site.register(EpayPayment)
|
||||
admin.site.register(models.EpayCallback)
|
||||
admin.site.register(models.EpayPayment)
|
||||
|
||||
@admin.register(ProductCategory)
|
||||
|
||||
@admin.register(models.ProductCategory)
|
||||
class ProductCategoryAdmin(admin.ModelAdmin):
|
||||
list_display = [
|
||||
'name',
|
||||
]
|
||||
|
||||
|
||||
@admin.register(Product)
|
||||
@admin.register(models.Product)
|
||||
class ProductAdmin(admin.ModelAdmin):
|
||||
list_display = [
|
||||
'name',
|
||||
|
@ -23,10 +24,17 @@ class ProductAdmin(admin.ModelAdmin):
|
|||
|
||||
|
||||
class ProductInline(admin.TabularInline):
|
||||
model = OrderProductRelation
|
||||
model = models.OrderProductRelation
|
||||
|
||||
|
||||
@admin.register(Order)
|
||||
class TicketInline(admin.TabularInline):
|
||||
model = models.Ticket
|
||||
exclude = ['qrcode_base64']
|
||||
|
||||
|
||||
|
||||
|
||||
@admin.register(models.Order)
|
||||
class OrderAdmin(admin.ModelAdmin):
|
||||
list_display = [
|
||||
'user',
|
||||
|
@ -44,5 +52,16 @@ class OrderAdmin(admin.ModelAdmin):
|
|||
|
||||
exclude = ['products']
|
||||
|
||||
inlines = [ProductInline]
|
||||
inlines = [ProductInline, TicketInline]
|
||||
|
||||
actions = ['mark_order_as_paid']
|
||||
|
||||
def mark_order_as_paid(self, request, queryset):
|
||||
for order in queryset.filter(paid=False):
|
||||
order.mark_as_paid()
|
||||
mark_order_as_paid.description = 'Mark order(s) as paid'
|
||||
|
||||
|
||||
@admin.register(models.Ticket)
|
||||
class TicketAdmin(admin.ModelAdmin):
|
||||
pass
|
30
shop/migrations/0012_ticket.py
Normal file
30
shop/migrations/0012_ticket.py
Normal file
|
@ -0,0 +1,30 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.9.6 on 2016-05-23 15:08
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import uuid
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('shop', '0011_auto_20160517_1902'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Ticket',
|
||||
fields=[
|
||||
('created', models.DateTimeField(auto_now_add=True)),
|
||||
('updated', models.DateTimeField(auto_now=True)),
|
||||
('uuid', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
|
||||
('order', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='shop.Order')),
|
||||
('product', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='shop.Product')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
]
|
20
shop/migrations/0013_ticket_qrcode_base64.py
Normal file
20
shop/migrations/0013_ticket_qrcode_base64.py
Normal file
|
@ -0,0 +1,20 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.9.6 on 2016-05-25 16:58
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('shop', '0012_ticket'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='ticket',
|
||||
name='qrcode_base64',
|
||||
field=models.TextField(blank=True, null=True),
|
||||
),
|
||||
]
|
21
shop/migrations/0014_ticket_name.py
Normal file
21
shop/migrations/0014_ticket_name.py
Normal file
|
@ -0,0 +1,21 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.9.6 on 2016-05-25 17:02
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('shop', '0013_ticket_qrcode_base64'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='ticket',
|
||||
name='name',
|
||||
field=models.CharField(default='', help_text=b'Name of the person this ticket belongs to. This can be different from the buying user.', max_length=100),
|
||||
preserve_default=False,
|
||||
),
|
||||
]
|
|
@ -1,3 +1,4 @@
|
|||
from django.conf import settings
|
||||
from django.db import models
|
||||
from django.db.models.aggregates import Sum
|
||||
from django.contrib.postgres.fields import DateTimeRangeField, JSONField
|
||||
|
@ -8,6 +9,12 @@ from django.core.urlresolvers import reverse_lazy
|
|||
from bornhack.utils import CreatedUpdatedModel, UUIDModel
|
||||
from .managers import ProductQuerySet
|
||||
|
||||
import hashlib
|
||||
import io
|
||||
import base64
|
||||
|
||||
import qrcode
|
||||
|
||||
|
||||
class Order(CreatedUpdatedModel):
|
||||
|
||||
|
@ -101,6 +108,21 @@ class Order(CreatedUpdatedModel):
|
|||
def get_absolute_url(self):
|
||||
return str(reverse_lazy('shop:order_detail', kwargs={'pk': self.pk}))
|
||||
|
||||
def mark_as_paid(self):
|
||||
self.paid = True
|
||||
for order_product in self.orderproductrelation_set.all():
|
||||
category_pk = str(order_product.product.category.pk)
|
||||
print(order_product, category_pk, settings.TICKET_CATEGORY_ID)
|
||||
if category_pk == settings.TICKET_CATEGORY_ID:
|
||||
for _ in range(0, order_product.quantity):
|
||||
ticket = Ticket(
|
||||
order=self,
|
||||
product=order_product.product,
|
||||
)
|
||||
ticket.save()
|
||||
self.save()
|
||||
|
||||
|
||||
|
||||
class ProductCategory(CreatedUpdatedModel, UUIDModel):
|
||||
class Meta:
|
||||
|
@ -222,3 +244,47 @@ class CoinifyCallback(CreatedUpdatedModel):
|
|||
def __str__(self):
|
||||
return 'callback at %s' % self.created
|
||||
|
||||
|
||||
class Ticket(CreatedUpdatedModel, UUIDModel):
|
||||
order = models.ForeignKey('shop.Order')
|
||||
product = models.ForeignKey('shop.Product')
|
||||
qrcode_base64 = models.TextField(null=True, blank=True)
|
||||
|
||||
name = models.CharField(
|
||||
max_length=100,
|
||||
help_text=(
|
||||
'Name of the person this ticket belongs to. '
|
||||
'This can be different from the buying user.'
|
||||
),
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return 'Ticket {user} {product}'.format(
|
||||
user=self.order.user,
|
||||
product=self.product
|
||||
)
|
||||
|
||||
def save(self, **kwargs):
|
||||
super(Ticket, self).save(**kwargs)
|
||||
self.qrcode_base64 = self.get_qr_code()
|
||||
super(Ticket, self).save(**kwargs)
|
||||
|
||||
def get_token(self):
|
||||
return hashlib.sha256(
|
||||
'{ticket_id}{user_id}{secret_key}'.format(
|
||||
ticket_id=self.pk,
|
||||
user_id=self.order.user.pk,
|
||||
secret_key=settings.SECRET_KEY,
|
||||
)
|
||||
).hexdigest()
|
||||
|
||||
def get_qr_code(self):
|
||||
qr = qrcode.make(self.get_token())
|
||||
file_like = io.BytesIO()
|
||||
qr.save(file_like)
|
||||
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)
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{% extends 'base.html' %}
|
||||
{% extends 'shop_base.html' %}
|
||||
{% load bootstrap3 %}
|
||||
|
||||
{% block content %}
|
||||
{% block shop_content %}
|
||||
|
||||
<h2>Pay by Bank Transfer</h2>
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
{% extends 'base.html' %}
|
||||
{% extends 'shop_base.html' %}
|
||||
|
||||
{% load bootstrap3 %}
|
||||
|
||||
{% block content %}
|
||||
{% block shop_content %}
|
||||
|
||||
<form method="POST">
|
||||
{% csrf_token %}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{% extends "base.html" %}
|
||||
{% extends "shop_base.html" %}
|
||||
{% load bootstrap3 %}
|
||||
{% block content %}
|
||||
{% block shop_content %}
|
||||
{% bootstrap_javascript jquery=1 %}
|
||||
<script charset="UTF-8" src="https://ssl.ditonlinebetalingssystem.dk/integration/ewindow/paymentwindow.js" type="text/javascript"></script>
|
||||
<script type="text/javascript">
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{% extends 'base.html' %}
|
||||
{% extends 'shop_base.html' %}
|
||||
{% load bootstrap3 %}
|
||||
|
||||
{% block content %}
|
||||
{% block shop_content %}
|
||||
<h2>Thank you for your payment!</h2>
|
||||
<p class="lead">
|
||||
{% if order.paid %}
|
||||
|
|
16
shop/templates/law/base.html
Normal file
16
shop/templates/law/base.html
Normal file
|
@ -0,0 +1,16 @@
|
|||
{% extends 'shop_base.html' %}
|
||||
|
||||
{% block shop_content %}
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-12 col-md-12 col-lg-12">
|
||||
<a href="{% url 'shop:privacy-policy' %}">Privacy Policy</a><br />
|
||||
<a href="{% url 'shop:return-policy' %}">Return Policy</a><br />
|
||||
<a href="{% url 'shop:general-terms' %}">General Terms & Conditions</a><br />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% block law-content %}
|
||||
{% endblock %}
|
||||
|
||||
{% endblock %}
|
9
shop/templates/law/general_terms_and_conditions.html
Normal file
9
shop/templates/law/general_terms_and_conditions.html
Normal file
|
@ -0,0 +1,9 @@
|
|||
{% extends 'law/base.html' %}
|
||||
|
||||
{# Forretningsbetingelser / General terms and conditions #}
|
||||
|
||||
{% block law-content %}
|
||||
|
||||
<h1>General Terms & Conditions</h1>
|
||||
|
||||
{% endblock %}
|
9
shop/templates/law/privacy_policy.html
Normal file
9
shop/templates/law/privacy_policy.html
Normal file
|
@ -0,0 +1,9 @@
|
|||
{% extends 'law/base.html' %}
|
||||
|
||||
{# Privatlivspolitik / Privacy policy #}
|
||||
|
||||
{% block law-content %}
|
||||
|
||||
<h1>Privacy Policy</h1>
|
||||
|
||||
{% endblock %}
|
9
shop/templates/law/return_policy.html
Normal file
9
shop/templates/law/return_policy.html
Normal file
|
@ -0,0 +1,9 @@
|
|||
{% extends 'law/base.html' %}
|
||||
|
||||
{# Returbetingelser / Return policy #}
|
||||
|
||||
{% block law-content %}
|
||||
|
||||
<h1>Return Policy</h1>
|
||||
|
||||
{% endblock %}
|
|
@ -1,8 +1,8 @@
|
|||
{% extends 'base.html' %}
|
||||
{% extends 'shop_base.html' %}
|
||||
{% load bootstrap3 %}
|
||||
{% load shop_tags %}
|
||||
|
||||
{% block content %}
|
||||
{% block shop_content %}
|
||||
|
||||
<h1>Order #{{ order.id }}</h1>
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{% extends 'base.html' %}
|
||||
{% extends 'shop_base.html' %}
|
||||
{% load bootstrap3 %}
|
||||
|
||||
{% block content %}
|
||||
{% block shop_content %}
|
||||
<h3>Orders</h3>
|
||||
<table class="table table-bordered table-hover">
|
||||
<thead>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{% extends 'base.html' %}
|
||||
{% extends 'shop_base.html' %}
|
||||
{% load bootstrap3 %}
|
||||
|
||||
{% block content %}
|
||||
{% block shop_content %}
|
||||
|
||||
<h2>{{ product.name }}</h2>
|
||||
|
||||
|
|
14
shop/templates/shop_base.html
Normal file
14
shop/templates/shop_base.html
Normal file
|
@ -0,0 +1,14 @@
|
|||
{% extends 'base.html' %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
{% block shop_content %}
|
||||
{% endblock %}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block footer %}
|
||||
<a href="{% url 'shop:privacy-policy' %}">Privacy Policy</a> |
|
||||
<a href="{% url 'shop:return-policy' %}">Return Policy</a> |
|
||||
<a href="{% url 'shop:general-terms' %}">General Terms & Conditions</a>
|
||||
{% endblock %}
|
|
@ -1,8 +1,8 @@
|
|||
{% extends 'base.html' %}
|
||||
{% extends 'shop_base.html' %}
|
||||
{% load bootstrap3 %}
|
||||
{% load shop_tags %}
|
||||
|
||||
{% block content %}
|
||||
{% block shop_content %}
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
|
|
|
@ -18,4 +18,8 @@ urlpatterns = [
|
|||
url(r'orders/(?P<pk>[0-9]+)/pay/blockchain/thanks/$', CoinifyThanksView.as_view(), name='coinify_thanks'),
|
||||
|
||||
url(r'orders/(?P<pk>[0-9]+)/pay/banktransfer/$', BankTransferView.as_view(), name='bank_transfer'),
|
||||
|
||||
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'general-terms-and-conditions/', TemplateView.as_view(template_name='law/general_terms_and_conditions.html'), name='general-terms')
|
||||
]
|
||||
|
|
Loading…
Reference in a new issue