shop pokus
This commit is contained in:
parent
2c2699de1f
commit
6763e773a6
@ -0,0 +1,125 @@
|
||||
# Generated by Django 4.2.12 on 2024-10-13 05:52
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('alkatorapi', '0018_profile_phone'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Cart',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Invoice',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('invoice_id', models.IntegerField(unique=True)),
|
||||
('total_price', models.IntegerField()),
|
||||
('paid_date', models.DateTimeField(auto_now=True)),
|
||||
('paid', models.BooleanField(default=False)),
|
||||
('trans_id', models.CharField(blank=True, max_length=120, null=True)),
|
||||
('address', models.CharField(blank=True, max_length=255, null=True)),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Product',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=120)),
|
||||
('description', models.TextField()),
|
||||
('img', models.CharField(max_length=120)),
|
||||
('price', models.IntegerField()),
|
||||
('quantity', models.IntegerField()),
|
||||
],
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='racer',
|
||||
name='id',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='racer',
|
||||
name='invoice_id',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='racer',
|
||||
name='paid',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='racer',
|
||||
name='price',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='racer',
|
||||
name='register_date',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='racer',
|
||||
name='trans_id',
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='racer',
|
||||
name='profile',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.RESTRICT, related_name='racers', to='alkatorapi.profile'),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='InvoiceProduct',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('quantity', models.IntegerField()),
|
||||
('price', models.IntegerField(default=1)),
|
||||
('invoice', models.ForeignKey(on_delete=django.db.models.deletion.RESTRICT, to='alkatorapi.invoice')),
|
||||
('product', models.ForeignKey(on_delete=django.db.models.deletion.RESTRICT, to='alkatorapi.product')),
|
||||
],
|
||||
options={
|
||||
'unique_together': {('product', 'invoice')},
|
||||
},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='invoice',
|
||||
name='items',
|
||||
field=models.ManyToManyField(through='alkatorapi.InvoiceProduct', to='alkatorapi.product'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='invoice',
|
||||
name='user',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.RESTRICT, to=settings.AUTH_USER_MODEL),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='CartProduct',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('quantity', models.IntegerField()),
|
||||
('cart', models.ForeignKey(on_delete=django.db.models.deletion.RESTRICT, to='alkatorapi.cart')),
|
||||
('product', models.ForeignKey(on_delete=django.db.models.deletion.RESTRICT, to='alkatorapi.product')),
|
||||
],
|
||||
options={
|
||||
'unique_together': {('product', 'cart')},
|
||||
},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='cart',
|
||||
name='items',
|
||||
field=models.ManyToManyField(through='alkatorapi.CartProduct', to='alkatorapi.product'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='cart',
|
||||
name='user',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.RESTRICT, to=settings.AUTH_USER_MODEL),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='racer',
|
||||
name='invoiceproduct_ptr',
|
||||
field=models.OneToOneField(auto_created=True, default=1, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='alkatorapi.invoiceproduct'),
|
||||
preserve_default=False,
|
||||
),
|
||||
]
|
@ -1,6 +1,7 @@
|
||||
from django.db import models
|
||||
from django.contrib import admin
|
||||
from django.contrib.auth.models import User as DjangoUser
|
||||
from django.core.exceptions import ValidationError
|
||||
|
||||
ALKATOR_CHOICES = (
|
||||
(1, "Alkátor"),
|
||||
@ -56,23 +57,81 @@ class Profile(models.Model):
|
||||
return f"<Profile {self.user.email} {self.first_name} {self.last_name}>"
|
||||
|
||||
|
||||
class Racer(models.Model):
|
||||
profile = models.ForeignKey(Profile, related_name='racers', on_delete=models.CASCADE)
|
||||
class Product(models.Model):
|
||||
name = models.CharField(max_length=120)
|
||||
description = models.TextField()
|
||||
img = models.CharField(max_length=120)
|
||||
price = models.IntegerField()
|
||||
quantity = models.IntegerField()
|
||||
|
||||
|
||||
class Cart(models.Model):
|
||||
user = models.ForeignKey(DjangoUser, on_delete=models.RESTRICT)
|
||||
items = models.ManyToManyField(Product, through='CartProduct')
|
||||
|
||||
|
||||
class CartProduct(models.Model):
|
||||
product = models.ForeignKey(Product, on_delete=models.RESTRICT)
|
||||
cart = models.ForeignKey('Cart', on_delete=models.RESTRICT)
|
||||
class Meta:
|
||||
unique_together = ('product', 'cart')
|
||||
quantity = models.IntegerField()
|
||||
|
||||
def clean(self):
|
||||
data = self.cleaned_data
|
||||
if data['quantity'] <= 0:
|
||||
raise ValidationError("Počet předmětů v košíku musí být kladný!")
|
||||
|
||||
|
||||
class Invoice(models.Model):
|
||||
invoice_id = models.IntegerField(unique=True)
|
||||
user = models.ForeignKey(DjangoUser, on_delete=models.RESTRICT)
|
||||
items = models.ManyToManyField(Product, through='InvoiceProduct')
|
||||
total_price = models.IntegerField()
|
||||
paid_date = models.DateTimeField(auto_now=True)
|
||||
paid = models.BooleanField(default=False)
|
||||
trans_id = models.CharField(null=True, blank=True, max_length=120)
|
||||
address = models.CharField(max_length=255, null=True, blank=True)
|
||||
|
||||
def calculate_total_price(self):
|
||||
total_price = 0
|
||||
for item in InvoiceProduct.objects.filter(invoice=self):
|
||||
total_price += item.price * item.quantity
|
||||
return total_price
|
||||
|
||||
|
||||
class InvoiceProduct(models.Model):
|
||||
product = models.ForeignKey(Product, on_delete=models.RESTRICT)
|
||||
invoice = models.ForeignKey(Invoice, on_delete=models.RESTRICT)
|
||||
class Meta:
|
||||
unique_together = ('product', 'invoice')
|
||||
quantity = models.IntegerField()
|
||||
price = models.IntegerField(default=1)
|
||||
|
||||
def clean(self):
|
||||
data = self.cleaned_data
|
||||
if data['quantity'] <= 0:
|
||||
raise ValidationError("Počet předmětů ve faktuře musí být kladný!")
|
||||
|
||||
|
||||
class Racer(InvoiceProduct):
|
||||
profile = models.ForeignKey(Profile, related_name='racers', on_delete=models.RESTRICT)
|
||||
first_name = models.CharField(max_length=120)
|
||||
last_name = models.CharField(max_length=120)
|
||||
email = models.EmailField(max_length=120, null=True, blank=True)
|
||||
team = models.CharField(max_length=120, null=True, blank=True)
|
||||
phone = models.CharField(max_length=120, null=True, blank=True)
|
||||
date_of_birth = models.DateField(null=True, blank=True)
|
||||
register_date = models.DateTimeField(auto_now=True)
|
||||
duration = models.DurationField(null=True, blank=True)
|
||||
starting_number = models.IntegerField(null=True, blank=True)
|
||||
alkator_category = models.IntegerField(choices=ALKATOR_CHOICES, default=1)
|
||||
alkator_class = models.IntegerField(choices=ALKATOR_CLASSES)
|
||||
trans_id = models.CharField(null=True, blank=True, max_length=120)
|
||||
price = models.IntegerField(default=690)
|
||||
paid = models.BooleanField(default=False)
|
||||
invoice_id = models.IntegerField(null=True, blank=True, unique=True)
|
||||
|
||||
def clean(self):
|
||||
super(self).clean()
|
||||
data = self.cleaned_data
|
||||
if data['quantity'] != 1:
|
||||
raise ValidationError("Počet přihlášek v jedné objednávce musí být právě 1!")
|
||||
|
||||
def __str__(self):
|
||||
return f"<Racer {self.email} {self.first_name} {self.last_name} {self.team}>"
|
||||
|
@ -21,7 +21,7 @@
|
||||
<tr>
|
||||
<td class="tg-7zrl" colspan="6"></td>
|
||||
<td class="tg-7zrl">Číslo faktury</td>
|
||||
<td class="tg-8d8j" colspan="2">{{racer.invoice_id}}</td>
|
||||
<td class="tg-8d8j" colspan="2">{{invoice.invoice_id}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tg-uzm7"><span style="background-color:#BFFE8D">Dodavatel</span></td>
|
||||
@ -166,7 +166,7 @@
|
||||
<td class="tg-7zrl" colspan="2">131-2219860207/0100</td>
|
||||
<td class="tg-7zrl"></td>
|
||||
<td class="tg-hvob" colspan="2"><span style="background-color:#A8DEF1">Datum vystavení</span></td>
|
||||
<td class="tg-2b7s" colspan="2">{{racer.register_date|date:"d/m/Y"}}</td>
|
||||
<td class="tg-2b7s" colspan="2">{{invoice.paid_date|date:"d/m/Y"}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tg-hvob" colspan="2"><span style="background-color:#A8DEF1">Variabilní symbol</span></td>
|
||||
@ -210,61 +210,19 @@
|
||||
<td class="tg-2b7s" colspan="2">Cena za MJ</td>
|
||||
<td class="tg-2b7s" colspan="2">Cena</td>
|
||||
</tr>
|
||||
{% for product in products %}
|
||||
<tr>
|
||||
<td class="tg-71pv">Startovné do závodu - ALKATOR</td>
|
||||
<td class="tg-71pv">{{product.product.name}}</td>
|
||||
<td class="tg-7zrl"></td>
|
||||
<td class="tg-7zrl"></td>
|
||||
<td class="tg-7zrl"></td>
|
||||
<td class="tg-2b7s">1</td>
|
||||
<td class="tg-7zrl"></td>
|
||||
<td class="tg-2b7s">{{racer.price}},00 Kč</td>
|
||||
<td class="tg-7zrl"></td>
|
||||
<td class="tg-2b7s">{{racer.price}},00 Kč</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tg-7zrl"></td>
|
||||
<td class="tg-7zrl"></td>
|
||||
<td class="tg-7zrl"></td>
|
||||
<td class="tg-7zrl"></td>
|
||||
<td class="tg-7zrl"></td>
|
||||
<td class="tg-7zrl"></td>
|
||||
<td class="tg-7zrl"></td>
|
||||
<td class="tg-7zrl"></td>
|
||||
<td class="tg-7zrl"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tg-7zrl"></td>
|
||||
<td class="tg-7zrl"></td>
|
||||
<td class="tg-7zrl"></td>
|
||||
<td class="tg-7zrl"></td>
|
||||
<td class="tg-7zrl"></td>
|
||||
<td class="tg-7zrl"></td>
|
||||
<td class="tg-7zrl"></td>
|
||||
<td class="tg-7zrl"></td>
|
||||
<td class="tg-7zrl"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tg-7zrl"></td>
|
||||
<td class="tg-7zrl"></td>
|
||||
<td class="tg-7zrl"></td>
|
||||
<td class="tg-7zrl"></td>
|
||||
<td class="tg-7zrl"></td>
|
||||
<td class="tg-7zrl"></td>
|
||||
<td class="tg-7zrl"></td>
|
||||
<td class="tg-7zrl"></td>
|
||||
<td class="tg-7zrl"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tg-7zrl"></td>
|
||||
<td class="tg-7zrl"></td>
|
||||
<td class="tg-7zrl"></td>
|
||||
<td class="tg-7zrl"></td>
|
||||
<td class="tg-7zrl"></td>
|
||||
<td class="tg-7zrl"></td>
|
||||
<td class="tg-7zrl"></td>
|
||||
<td class="tg-7zrl"></td>
|
||||
<td class="tg-2b7s">{{product.price}},00 Kč</td>
|
||||
<td class="tg-7zrl"></td>
|
||||
<td class="tg-2b7s">{{product.price}},00 Kč</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
<tr>
|
||||
<td class="tg-7zrl"></td>
|
||||
<td class="tg-7zrl"></td>
|
||||
@ -272,7 +230,7 @@
|
||||
<td class="tg-7zrl"></td>
|
||||
<td class="tg-7zrl"></td>
|
||||
<td class="tg-hvob" colspan="2"><span style="background-color:#A8DEF1">Celkem</span></td>
|
||||
<td class="tg-kbc9" colspan="2"><span style="font-weight:bold;color:#FFF;background-color:#66B3ED">{{racer.price}},00 Kč</span></td>
|
||||
<td class="tg-kbc9" colspan="2"><span style="font-weight:bold;color:#FFF;background-color:#66B3ED">{{invoice.total_price}},00 Kč</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tg-7zrl"></td>
|
||||
|
@ -19,7 +19,7 @@ import PIL.Image
|
||||
import random
|
||||
from collections import OrderedDict
|
||||
|
||||
from .models import User, ALKATOR_CHOICES_DICT, ALKATOR_CLASSES, Profile, Racer
|
||||
from .models import User, ALKATOR_CHOICES_DICT, ALKATOR_CLASSES, Profile, Racer, Invoice, Product
|
||||
from alkator.settings import COMGATE_MERCHANT, COMGATE_SECRET, COMGATE_TEST
|
||||
|
||||
|
||||
@ -116,15 +116,15 @@ def register_racer(request):
|
||||
except User.DoesNotExist:
|
||||
invoice_id = invoice_id + 1
|
||||
|
||||
if date.today() >= date(2024, 9, 21):
|
||||
price = 79000
|
||||
else:
|
||||
price = 69000
|
||||
|
||||
profile = request.user.profile
|
||||
user = request.user
|
||||
|
||||
product = Product.objects.get(id=1)
|
||||
price = product.price
|
||||
|
||||
racer = Racer(
|
||||
product=product,
|
||||
quantity=1,
|
||||
profile = profile,
|
||||
first_name = request.POST['first_name'],
|
||||
last_name = request.POST['last_name'],
|
||||
@ -133,15 +133,22 @@ def register_racer(request):
|
||||
phone = request.POST['phone'],
|
||||
date_of_birth = dob,
|
||||
alkator_class = ALKATOR_CLASS,
|
||||
price = price // 100,
|
||||
invoice_id = invoice_id,
|
||||
price=price,
|
||||
)
|
||||
invoice = Invoice(
|
||||
invoice_id=invoice_id,
|
||||
user=user,
|
||||
items=[racer],
|
||||
total_price=price,
|
||||
address=profile.address,
|
||||
)
|
||||
racer.save()
|
||||
invoice.save()
|
||||
|
||||
payment_data = {
|
||||
'merchant': COMGATE_MERCHANT,
|
||||
'test': 'true' if COMGATE_TEST else 'false',
|
||||
'price': price,
|
||||
'price': price * 100,
|
||||
'curr': 'CZK',
|
||||
'method': 'ALL',
|
||||
'label': 'Startovné na závod Alkátor Race Dolní Čermná 2025',
|
||||
@ -157,10 +164,11 @@ def register_racer(request):
|
||||
|
||||
if result['code'][0] != '0':
|
||||
racer.delete()
|
||||
invoice.delete()
|
||||
return HttpResponse('{"reason":"Chyba na straně platební brány: ' + result['message'][0] + ', zkuste prosím registraci později."}', status=400, content_type='application/json')
|
||||
|
||||
racer.trans_id = result['transId'][0]
|
||||
racer.save()
|
||||
invoice.trans_id = result['transId'][0]
|
||||
invoice.save()
|
||||
|
||||
return HttpResponse('{"success":"", "redirect":"' + result['redirect'][0] + '"}', content_type='application/json')
|
||||
|
||||
@ -177,14 +185,14 @@ def login_status(request):
|
||||
"last_name": user.profile.last_name,
|
||||
"address": user.profile.address,
|
||||
"racers": [{
|
||||
"invoice_id": racer.invoice_id,
|
||||
"invoice_id": racer.invoice.invoice_id,
|
||||
"first_name": racer.first_name,
|
||||
"last_name": racer.last_name,
|
||||
"email": racer.email,
|
||||
"phone": racer.phone,
|
||||
"team": racer.team,
|
||||
"date_of_birth": racer.date_of_birth.strftime("%Y-%m-%d"),
|
||||
"paid": racer.paid,
|
||||
"paid": racer.invoice.paid,
|
||||
} for racer in racers]
|
||||
}), content_type='application/json')
|
||||
|
||||
@ -231,13 +239,14 @@ def payment_result(request):
|
||||
if not secret_match or test != COMGATE_TEST:
|
||||
return HttpResponse(status=400)
|
||||
try:
|
||||
racer = Racer.objects.get(invoice_id=ref_id)
|
||||
except Racer.DoesNotExist:
|
||||
invoice = Invoice.objects.get(invoice_id=ref_id)
|
||||
except Invoice.DoesNotExist:
|
||||
mail_admins('Chyba s platbou!', f'invoice_id={ref_id}&paid={paid}')
|
||||
return HttpResponse(status=404)
|
||||
if paid == 'PAID':
|
||||
racer.paid = True
|
||||
racer.save()
|
||||
invoice.paid = True
|
||||
invoice.save()
|
||||
racer = Racer.objects.get(invoice=invoice)
|
||||
mail = EmailMessage(
|
||||
subject=f"úspěšná registrace do závodu Alkátor race Dolní Čermná ({racer.first_name} {racer.last_name})",
|
||||
body=f"""Zdravím tě Alkátore,
|
||||
@ -265,14 +274,15 @@ web: https://alkator.cz""",
|
||||
cc=[]
|
||||
)
|
||||
|
||||
user = racer.profile.user
|
||||
user = invoice.user
|
||||
|
||||
template = TemplateResponse(
|
||||
None,
|
||||
'invoice.html',
|
||||
{
|
||||
'user': user,
|
||||
'racer': racer,
|
||||
'invoice': invoice,
|
||||
'products': InvoiceProduct.filter(invoice=invoice),
|
||||
'paid_date': racer.register_date + timedelta(days=1),
|
||||
}
|
||||
)
|
||||
@ -282,7 +292,8 @@ web: https://alkator.cz""",
|
||||
|
||||
attach = open(pdf_name, 'rb')
|
||||
mail.attach('faktura.pdf', attach.read(), 'application/pdf')
|
||||
mail.send()
|
||||
if not mail.send():
|
||||
return HttpResponse(status=500)
|
||||
elif paid == 'CANCELLED' and not user.paid:
|
||||
racer.delete()
|
||||
return HttpResponse(status=200)
|
||||
@ -362,12 +373,14 @@ def upload_files(request):
|
||||
|
||||
@staff_member_required
|
||||
def invoice(request):
|
||||
user = User.objects.get(invoice_id=request.GET['invoice_id'])
|
||||
user = Invoice.objects.get(invoice_id=request.GET['invoice_id'])
|
||||
return TemplateResponse(
|
||||
None,
|
||||
'invoice.html',
|
||||
{
|
||||
'user': user,
|
||||
'paid_date': user.register_date + timedelta(days=1),
|
||||
'invoice': invoice,
|
||||
'products': InvoiceProduct.filter(invoice=invoice),
|
||||
'paid_date': racer.register_date + timedelta(days=1),
|
||||
}
|
||||
)
|
||||
|
@ -1,28 +0,0 @@
|
||||
from datetime import date, datetime, timedelta
|
||||
from django.template.response import TemplateResponse
|
||||
from alkatorapi.models import User
|
||||
from weasyprint import HTML
|
||||
from pypdf import PdfWriter
|
||||
import asyncio
|
||||
|
||||
def html_to_pdf(html_content, output_path):
|
||||
HTML(string=html_content).write_pdf(output_path)
|
||||
|
||||
merger = PdfWriter()
|
||||
|
||||
for user in User.objects.all().order_by('last_name', 'first_name'):
|
||||
template = TemplateResponse(
|
||||
None,
|
||||
'invoice.html',
|
||||
{
|
||||
'user': user,
|
||||
'paid_date': user.register_date + timedelta(days=1),
|
||||
}
|
||||
)
|
||||
template.render()
|
||||
pdf_name = f"{user.last_name}_{user.first_name}.pdf"
|
||||
html_to_pdf(template.content, pdf_name)
|
||||
merger.append(pdf_name)
|
||||
|
||||
merger.write('result.pdf')
|
||||
merger.close()
|
Loading…
x
Reference in New Issue
Block a user