conference-website/tickets/views/invoice.py

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