98 lines
3.6 KiB
Python
98 lines
3.6 KiB
Python
import io
|
|
import os.path
|
|
import logging
|
|
import re
|
|
import typing
|
|
|
|
from django.conf import settings
|
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
|
from django.template.loader import get_template
|
|
from django.template.response import TemplateResponse
|
|
from django.views.generic import DetailView
|
|
from weasyprint import HTML, CSS
|
|
from weasyprint.text.fonts import FontConfiguration
|
|
|
|
from tickets.views.mixins import TicketBoughtRequiredMixin
|
|
import tickets.models
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class PDFResponse(TemplateResponse):
|
|
md5_re = re.compile(r'\.[0-9a-f]{12}(\.[^.]+)$')
|
|
font_path = os.path.join(settings.BASE_DIR, 'tickets', 'templates', 'tickets')
|
|
fonts = (
|
|
('normal', 'normal', os.path.join(font_path, 'roboto.regular.ttf')),
|
|
('bold', 'normal', os.path.join(font_path, 'roboto.bold.ttf')),
|
|
('normal', 'italic, oblique', os.path.join(font_path, 'roboto.italic.ttf')),
|
|
('lighter', 'normal', os.path.join(font_path, 'roboto.light.ttf')),
|
|
('bold', 'italic, oblique', os.path.join(font_path, 'roboto.bold-italic.ttf')),
|
|
('normal', 'monospace', os.path.join(font_path, 'robotomono.regular.ttf')),
|
|
)
|
|
|
|
def __init__(
|
|
self,
|
|
request,
|
|
template,
|
|
context=None,
|
|
content_type: typing.Optional[str] = None,
|
|
status=None,
|
|
charset=None,
|
|
using=None,
|
|
) -> None:
|
|
if content_type is None:
|
|
content_type = 'application/pdf'
|
|
super().__init__(request, template, context, content_type, status, charset, using)
|
|
|
|
@property
|
|
def rendered_content(self) -> bytes:
|
|
"""Return the freshly rendered content for the template and context
|
|
described by the TemplateResponse.
|
|
|
|
This *does not* set the final content of the response. To set the
|
|
response content, you must either call render(), or set the
|
|
content explicitly using the value of this property.
|
|
"""
|
|
template = self.resolve_template(self.template_name)
|
|
context = self.resolve_context(self.context_data)
|
|
content = template.render(context, self._request)
|
|
|
|
font_config = FontConfiguration()
|
|
|
|
assert all(os.path.isfile(_[-1]) for _ in self.fonts)
|
|
css_content = get_template('tickets/invoice.css').render({'fonts': self.fonts})
|
|
css = CSS(string=css_content, font_config=font_config)
|
|
response = io.BytesIO()
|
|
HTML(string=content, base_url=self._request.build_absolute_uri()).write_pdf(
|
|
target=response, stylesheets=[css], font_config=font_config
|
|
)
|
|
return response.getvalue()
|
|
|
|
|
|
class PDFView(LoginRequiredMixin, TicketBoughtRequiredMixin, DetailView):
|
|
"""Return a PDF invoice rendered from Stripe checkout session data."""
|
|
|
|
template_name = 'tickets/invoice.html'
|
|
model = tickets.models.Ticket
|
|
slug_field = 'token'
|
|
slug_url_kwarg = 'ticket_token'
|
|
context_object_name = 'ticket'
|
|
response_class = PDFResponse
|
|
|
|
def get_context_data(self, **kwargs):
|
|
"""Fetch order via API and add it to the context."""
|
|
context = super().get_context_data(**kwargs)
|
|
ticket = self.object
|
|
order = ticket.order
|
|
context['order'] = order
|
|
|
|
payment_intent = order.payment_intent
|
|
charge = payment_intent.latest_charge
|
|
context['charge'] = charge
|
|
context['customer_details'] = order.customer_details
|
|
context['billing_details'] = context['customer_details']
|
|
if charge:
|
|
context['billing_details'] = charge.billing_details
|
|
context['tax_details'] = ticket.get_tax_details(order)
|
|
return context
|