Implement PDF generator

Improve mail view
Implement voucher generation
This commit is contained in:
Kumi 2021-05-31 07:20:27 +02:00
parent 29231bcfc9
commit a3d31c5cc3
16 changed files with 247 additions and 21 deletions

View file

@ -1,5 +1,5 @@
#!/bin/bash
apt install libpq-dev build-essential libpython3-dev postgis python3-pip python3-venv libgdal-dev -y
apt install libpq-dev build-essential libpython3-dev postgis python3-pip python3-venv libgdal-dev wkhtmltopdf -y
python3 -m venv venv
source venv/bin/activate
pip install -Ur requirements.txt

View file

@ -51,6 +51,6 @@ class VerificationView(SuperUserRequiredMixin, FormView):
return HttpResponseRedirect(resolve_url("localauth:verify"))
class EmailVerificationHTMLMailView(MailView):
class EmailVerificationMailView(MailView):
template_name = "localauth/mail/verify.html"

View file

@ -9,28 +9,41 @@ from html.parser import HTMLParser
from bs4 import BeautifulSoup
class MailView(ContextMixin):
def __init__(self, template_name=None, html_template_name=None, text_template_name=None):
self.html_template_name = html_template_name
self.text_template_name = text_template_name
template_name = None
if template_name:
self._template_name = template_name
if len(template_name.split("/")[-1].split(".")[-1] in ("html", "txt")):
@property
def html_template_name(self):
if self.template_name:
if self.template_name.split("/")[-1].split(".")[-1] in ("html", "txt"):
basename = template_name.rsplit(".", 1)[0]
else:
basename = template_name
for ext in ("html", "txt"):
try:
path = f"{basename}.{ext}"
render_to_string(path)
if ext == "html":
self.html_template_name = self.html_template_name or path
else:
self.text_template_name = self.text_template_name or path
except TemplateDoesNotExist:
pass
try:
path = f"{basename}.html"
render_to_string(path)
return path
except TemplateDoesNotExist:
pass
return False
@property
def text_template_name(self):
if self.template_name:
if self.template_name.split("/")[-1].split(".")[-1] in ("html", "txt"):
basename = template_name.rsplit(".", 1)[0]
else:
basename = template_name
try:
path = f"{basename}.txt"
render_to_string(path)
return path
except TemplateDoesNotExist:
pass
return False
def render_to_html(self, **kwargs):
if self.html_template_name:

View file

@ -7,5 +7,6 @@ app_name = "payment"
urlpatterns = [
path('gateways/paypal/', include("payment.paypal.urls"), name="paypal"),
path('gateways/sepa/', include("payment.sepa.urls"), name="sepa"),
path('gateways/voucher/', include("payment.voucher.urls"), name="voucher"),
path('status/<slug:uuid>/', PaymentStatusView.as_view(), name="status"),
]

View file

@ -1,13 +1,21 @@
from django.db import models
from django.contrib.auth import get_user_model
from django.urls import reverse_lazy
from .helpers import generate_voucher_code
from ..const import PAYMENT_STATUS_SUCCESS, PAYMENT_STATUS_FAILED, PAYMENT_STATUS_REFUNDED
from ..models import InvoicePayment
from payment.const import PAYMENT_STATUS_SUCCESS, PAYMENT_STATUS_FAILED, PAYMENT_STATUS_REFUNDED
from clients.models import ClientProfile
from dbsettings.functions import getValue
from datetime import timedelta
import uuid
class Voucher(models.Model):
code = models.IntegerField(default=generate_voucher_code)
code = models.BigIntegerField(default=generate_voucher_code, unique=True)
value = models.DecimalField(max_digits=10, decimal_places=2)
created_at = models.DateTimeField(auto_now_add=True)
@ -19,6 +27,21 @@ class Voucher(models.Model):
except VoucherActivation.DoesNotExist:
return False
def get_absolute_url(self):
return VoucherDownloadURL.objects.create(voucher=self).get_absolute_url()
class VoucherDownloadURL(models.Model):
uuid = models.UUIDField(default=uuid.uuid4, primary_key=True)
voucher = models.ForeignKey(Voucher, models.CASCADE)
created = models.DateTimeField(auto_now_add=True)
@property
def expiry(self):
return self.created + timedelta(seconds=getValue("payment.voucher.urlvalidity", 300))
def get_absolute_url(self):
return reverse_lazy("payment:voucher:download")
class VoucherActivation(models.Model):
voucher = models.OneToOneField(Voucher, models.PROTECT)
user = models.ForeignKey(get_user_model(), models.PROTECT)

10
payment/voucher/urls.py Normal file
View file

@ -0,0 +1,10 @@
from django.urls import path
from .views import AdminGenerateVoucherView, DownloadVoucherView
app_name = "voucher"
urlpatterns = [
path('generate/', AdminGenerateVoucherView.as_view(), name="generate"),
path('download/<slug:pk>/', DownloadVoucherView.as_view(), name="download"),
]

38
payment/voucher/views.py Normal file
View file

@ -0,0 +1,38 @@
from django.views.generic import CreateView, DetailView
from django.shortcuts import redirect
from django.utils import timezone
from django.http import Http404, HttpResponse
from localauth.mixins import SuperUserRequiredMixin
from pdf.views import PDFView
from .models import VoucherDownloadURL
class AdminGenerateVoucherView(SuperUserRequiredMixin, CreateView):
pass
class VoucherPDFView(PDFView):
template_name = "payment/voucher/pdfvoucher.html"
voucher = None
def get_context_data(self, **kwargs):
return super().get_context_data(voucher=self.voucher, **kwargs)
class DownloadVoucherView(DetailView):
model = VoucherDownloadURL
def get(self, request, *args, **kwargs):
obj = self.get_object()
if (not obj) or obj.expiry < timezone.now():
raise Http404()
pdfview = VoucherPDFView()
pdfview.voucher = obj.voucher
pdf = pdfview.render()
response = HttpResponse(pdf, content_type="application/pdf")
response['Content-Disposition'] = 'inline; filename=voucher.pdf'
return response

0
pdf/__init__.py Normal file
View file

3
pdf/admin.py Normal file
View file

@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

6
pdf/apps.py Normal file
View file

@ -0,0 +1,6 @@
from django.apps import AppConfig
class PdfConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'pdf'

3
pdf/models.py Normal file
View file

@ -0,0 +1,3 @@
from django.db import models
# Create your models here.

3
pdf/tests.py Normal file
View file

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

23
pdf/views.py Normal file
View file

@ -0,0 +1,23 @@
from django.views.generic.base import ContextMixin
from django.template.loader import render_to_string
from django.template.exceptions import TemplateDoesNotExist
from django.core.mail import EmailMultiAlternatives
from django.conf import settings
from html.parser import HTMLParser
from bs4 import BeautifulSoup
import pdfkit
class PDFView(ContextMixin):
template_name = None
def render_to_html(self, **kwargs):
context = self.get_context_data(**kwargs)
return render_to_string(self.template_name, context)
def render(self, **kwargs):
html = self.render_to_html(**kwargs)
pdf = pdfkit.from_string(html, False)
return pdf

View file

@ -23,4 +23,5 @@ django-filtersignals
python-magic
bs4
django-starfield
pdfkit
git+https://kumig.it/kumisystems/PyInvoice

Binary file not shown.

After

Width:  |  Height:  |  Size: 236 KiB

View file

@ -0,0 +1,102 @@
{% load static %}
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<style>
html {
margin-top: 0in !important;
margin-left: 0in !important;
}
@page {
margin: 0px;
}
body {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 13px;
line-height: 1.4em;
margin: 0px;
}
.voucher {
width: 8in;
height: 3.77in;
position: relative;
}
.voucher-img {
max-width: 100%;
height: auto;
position: relative;
}
.field-amount {
position: absolute;
top: 236px;
right: 353px;
color: #fff;
font-size: 57px;
line-height: 40px;
font-weight: 500;
}
.field-content {
position: absolute;
width: 40%;
right: 10px;
top: 48px;
text-align: center;
}
.demo-title {
font-size: 35px;
font-weight: 300;
line-height: 1;
}
.demo-p {
font-size: 16px;
font-weight: 400;
margin-top: 10px;
}
.field-code {
padding: 15px;
border: 1px dotted;
margin: 35px 0 15px;
font-size: 20px;
font-weight: 600;
}
.field-expiry {
padding: 8px 13px;
background: #eee;
}
.field-customer {
margin-top: 20px;
}
</style>
</head>
<body>
<div class="voucher">
<img class="voucher-img" src="{% static "payment/voucher/gift-card-img.jpg" %}" />
<span class="field-amount">{{ voucher.value }}</span>
<div class="field-content">
<div class="demo-title">JourneyJoker-Gutschein</div>
<div class="demo-p">Buche noch heute deinen nächsten Traumurlaub.</div>
<div class="field-code">{{ voucher.code }}</div>
<div class="field-expiry">Gutschein erstellt am {{ voucher.created_at }}</div>
<div class="field-customer" style="display:none;">Gekauft von {{ voucher.customer.user.name }}</div>
</div>
</div>
</body>
</html>