Source code for translator_directory.qrbill

from __future__ import annotations

from io import BytesIO
from onegov.translator_directory import log
from qrbill.bill import QRBill
from stdnum import iban as iban_validator
from weasyprint import HTML  # type: ignore[import-untyped]

from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from onegov.translator_directory.models.time_report import (
        TranslatorTimeReport,
    )
    from onegov.translator_directory.models.translator import Translator
    from onegov.translator_directory.request import TranslatorAppRequest


[docs] def is_valid_iban(value: str | None) -> bool: """Check if value is a valid IBAN.""" if not value: return False try: iban_validator.validate(value) return True except Exception: return False
[docs] def generate_translator_qr_bill( translator: Translator, time_report: TranslatorTimeReport, request: TranslatorAppRequest, ) -> bytes | None: """ Generates a QR Bill PDF for a self-employed translator. Returns PDF bytes or None if generation fails. """ if not translator.iban or not is_valid_iban(translator.iban): request.warning('Invalid or missing IBAN for QR bill generation') return None if not all( [ translator.first_name, translator.last_name, translator.address, translator.zip_code, translator.city, ] ): request.warning('Incomplete address for QR bill generation') log.error(f'Incomplete address for translator {translator.id}') return None # Creditor (translator) creditor = { 'name': f'{translator.first_name} {translator.last_name}', 'line1': translator.address, 'line2': f'{translator.zip_code} {translator.city}', } # Debtor (organization) - hardcoded for now # TODO: Move to organization settings debtor = { 'name': 'Zentrale Polizeistation', 'street': 'Beckstube 1', 'pcode': '8200', 'city': 'Schaffhausen', } # Amount amount = '{:.2f}'.format(time_report.total_compensation or 0) # Additional information additional_information = ( f'Zeitrapport {time_report.assignment_date.strftime("%d.%m.%Y")}' ) # Language language = 'de' # Default to German if hasattr(request, 'locale') and request.locale: locale_map = {'de_CH': 'de', 'fr_CH': 'fr', 'it_CH': 'it'} language = locale_map.get(request.locale, 'de') # Create QR bill try: bill = QRBill( # type: ignore[call-overload] account=translator.iban, creditor=creditor, debtor=debtor, amount=amount, additional_information=additional_information, language=language, ) except Exception as e: log.exception(e) return None # Generate SVG from io import StringIO svg = StringIO() bill.as_svg(svg) svg_content = svg.getvalue() # Convert SVG to PDF using WeasyPrint try: html_content = f""" <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <style> @page {{ size: A4; margin: 0; }} body {{ margin: 0; padding: 0; }} .qr-bill-container {{ width: 210mm; height: 297mm; display: flex; align-items: flex-end; }} </style> </head> <body> <div class="qr-bill-container"> {svg_content} </div> </body> </html> """ pdf_file = BytesIO() HTML(string=html_content).write_pdf(pdf_file) return pdf_file.getvalue() except Exception as e: log.exception(e) return None