Skip to content

10. PDF documents

A borrower leaves with a receipt. The module supplies the template; the kernel does the rest.

One file is enough

Drop reports/library_loan.html. The name carries everything: <model>.html is the model’s default report, <model>.<name>.html is another, named one.

{% extends "kewos:document" %}
{% block body %}
<h1>{{ t("library_loan.receipt.title") }}</h1>
<table class="kws-table">
<tbody>
<tr><th>{{ t("library_loan.member_id") }}</th><td>{{ record.member_id }}</td></tr>
<tr><th>{{ t("library_loan.copy_id") }}</th><td>{{ record.copy_id }}</td></tr>
<tr><th>{{ t("library_loan.taken_on") }}</th><td>{{ record.taken_on }}</td></tr>
<tr><th>{{ t("library_loan.due_on") }}</th><td><strong>{{ record.due_on }}</strong></td></tr>
<tr><th>{{ t("library_loan.state") }}</th>
<td>{{ t("library_loan.state." ~ record.state) }}</td></tr>
</tbody>
</table>
<p class="kws-muted">{{ t("library_loan.receipt.note") }}</p>
{% endblock %}

And one line in the module’s entry point:

from kewos.pdf import register_reports_from_dir
register_reports_from_dir(_MODULE_DIR / "reports")

What it produces

The loan receipt, rendered as a PDF with the company letterhead

The letterhead, the logo, the footer and the colours come from the company’s document theme. You wrote neither: {% extends "kewos:document" %} brings them, and a customer who changes their logo changes every document at once.

The print button appears on the model’s screen, with nothing more to declare.

The context: what the template receives

A report that needs no Python receives a context built generically:

In the templateWhat you get
record.<field>The value, stringified.
record.<relation>The label of the record it points at.
record.<children>The children, as dictionaries.
t("key")The translation, in the document’s language.

When the generic shape is not enough

Register the report yourself, with a context builder:

from kewos.pdf import register_model_report
def _receipt_context(session, classes, record_id):
"""Everything the receipt needs, including what is two hops away."""
loan = session.get(classes["library_loan"], record_id)
return {
"record": loan,
"title": loan.copy_id.title_id.name,
"member_card": loan.member_id.card_number,
}
register_model_report(
"library_loan",
code="library_loan",
title="Loan receipt",
body_template=TEMPLATE,
build_context=_receipt_context,
)

An operational trap, now fixed

Next

Reacting: acting on its own, every morning.