diff --git a/debian_setup.sh b/debian_setup.sh index 9562545..24e2a53 100644 --- a/debian_setup.sh +++ b/debian_setup.sh @@ -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 \ No newline at end of file diff --git a/localauth/views.py b/localauth/views.py index a6fb26a..c6b28cb 100644 --- a/localauth/views.py +++ b/localauth/views.py @@ -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" \ No newline at end of file diff --git a/mail/views.py b/mail/views.py index 53d507a..7642a93 100644 --- a/mail/views.py +++ b/mail/views.py @@ -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: diff --git a/payment/urls.py b/payment/urls.py index 949cfce..43f2296 100644 --- a/payment/urls.py +++ b/payment/urls.py @@ -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//', PaymentStatusView.as_view(), name="status"), ] diff --git a/payment/voucher/models.py b/payment/voucher/models.py index f117e45..0f5e419 100644 --- a/payment/voucher/models.py +++ b/payment/voucher/models.py @@ -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) diff --git a/payment/voucher/urls.py b/payment/voucher/urls.py new file mode 100644 index 0000000..9d1046d --- /dev/null +++ b/payment/voucher/urls.py @@ -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//', DownloadVoucherView.as_view(), name="download"), +] diff --git a/payment/voucher/views.py b/payment/voucher/views.py new file mode 100644 index 0000000..12eb3a0 --- /dev/null +++ b/payment/voucher/views.py @@ -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 \ No newline at end of file diff --git a/pdf/__init__.py b/pdf/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pdf/admin.py b/pdf/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/pdf/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/pdf/apps.py b/pdf/apps.py new file mode 100644 index 0000000..40d8c60 --- /dev/null +++ b/pdf/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class PdfConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'pdf' diff --git a/pdf/models.py b/pdf/models.py new file mode 100644 index 0000000..71a8362 --- /dev/null +++ b/pdf/models.py @@ -0,0 +1,3 @@ +from django.db import models + +# Create your models here. diff --git a/pdf/tests.py b/pdf/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/pdf/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/pdf/views.py b/pdf/views.py new file mode 100644 index 0000000..28df865 --- /dev/null +++ b/pdf/views.py @@ -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 \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 4278dfc..7e6d184 100644 --- a/requirements.txt +++ b/requirements.txt @@ -23,4 +23,5 @@ django-filtersignals python-magic bs4 django-starfield +pdfkit git+https://kumig.it/kumisystems/PyInvoice diff --git a/static/payment/voucher/gift-card-img.jpg b/static/payment/voucher/gift-card-img.jpg new file mode 100644 index 0000000..a11d92d Binary files /dev/null and b/static/payment/voucher/gift-card-img.jpg differ diff --git a/templates/payment/voucher/pdfvoucher.html b/templates/payment/voucher/pdfvoucher.html new file mode 100644 index 0000000..66a1357 --- /dev/null +++ b/templates/payment/voucher/pdfvoucher.html @@ -0,0 +1,102 @@ +{% load static %} + + + + + + + + +
+ + + {{ voucher.value }} + +
+
JourneyJoker-Gutschein
+
Buche noch heute deinen nächsten Traumurlaub.
+ +
{{ voucher.code }}
+
Gutschein erstellt am {{ voucher.created_at }}
+ +
+
+ + +