Document Report Page View
Developer guide for the Enezaframework reporting engine. Part of the Add Report series.
Table of Contents
Overview
The Document Report Page View feature lets you create document-type reports (header +
lines documents such as invoices, purchase orders, and statements) that render directly
from a detail view's Print dropdown. The report auto-populates with the current
document's data via URL pk, requiring no form submission and no query parameters.
Problem solved: Previously, document reports required the full form/parameter flow via
ReportEngineView; there was no dedicated view to render them from a URL path alone. The
Print dropdown was also absent, so developers had to misuse the general action_list for
print/report actions.
User outcome:
- Subclass
DocumentReportViewand setmodel,header_model,header_fields,line_columns,line_total_fields. - Optionally set
report_nameto control the URL name (one model can power many reports). - Decorate with
@auto_url_register. - The URL is auto-registered as
{app_name}:{report_name}_report, accepting<str:pk>. - Set
print_actionson the detail view — these appear in a Print dropdown (distinct from Actions). - The user clicks a Print option and the report renders immediately for that document in
#detail-containervia HTMX.
Architecture
flowchart TD
DEV["Developer writes one class:<br/>@auto_url_register<br/>class InvoiceReportView(DocumentReportView)"]
DEV --> BASE["DocumentReportView(BaseDocumentReport)<br/>single-parent base"]
BASE --> COMPAT["BaseDocumentReport(DocumentReport, DocumentReportPageView)"]
COMPAT --> PAGE["DocumentReportPageView(DesignTemplateMixin, ReportEngineView)<br/>get() → _get_document_report_results()<br/>render_to_response() → extract lines for designer"]
COMPAT --> URLS["get_registered_urls()<br/>view_type == 'document_report'<br/>URL: {report_name}_report/<str:pk>/"]
PAGE --> DETAIL["BaseDetailView<br/>print_actions = []<br/>context['print_actions']"]
DETAIL --> MIXIN["ActionMixin.get_print_action_config()<br/>dict list → template-safe format"]
MIXIN --> NAV["navactions.html<br/>Print dropdown (hx-get, no confirmation)"]Data Flow
sequenceDiagram
actor U as User
participant D as Detail view
participant V as DocumentReportPageView
participant G as DocumentReportGenerator
participant T as document_report.html
U->>D: click "Invoice Report" in Print dropdown
D->>V: HTMX hx-get /myapp/invoice_report/42/
V->>V: get(pk='42')
V->>G: DocumentReportGenerator.get_report_data(42)
G-->>V: {header, lines, totals}
V->>T: get_context_data(report_data=...) + render
T-->>U: header grid + lines table + totalsQuick Start
Step 1: Define the Report View (Single Class)
# myapp/views/report_views.py
from enezareporting.report_base import DocumentReportView
from enezaframework.utils import auto_url_register
from myapp.models import Invoice
@auto_url_register
class InvoiceReportView(DocumentReportView):
model = Invoice
report_name = "invoice"
header_model = Invoice
lines_related_name = "lines"
header_fields = ["invoice_number", "customer", "date", "due_date", "status"]
line_columns = ["item", "description", "quantity", "unit_price", "line_total"]
line_total_fields = ["line_total", "quantity"]
One import, one base class, one class definition. DocumentReportView subclasses the
compatibility BaseDocumentReport, which combines DocumentReport (report type semantics)
with DocumentReportPageView (view machinery). See
BaseDocumentReport.
Step 2: Add a Print Action to the Detail View
# myapp/views/invoice_views.py
from enezaframework.views import BaseSourceDocumentDetailView
from enezaframework.utils import auto_url_register
from myapp.models import Invoice
@auto_url_register
class InvoiceDetailView(BaseSourceDocumentDetailView):
model = Invoice
print_actions = [
{
"name": "Invoice Report",
"icon": "printer",
"url": "invoice_report", # matches report_name + '_report'
}
]
def detail_ui_view(self):
return [
{
"name": "Invoice Information",
"icon": "Home",
"Fieldcolumns": [
{"fields": ["invoice_number", "customer"]},
{"fields": ["date", "due_date", "status"]},
],
},
]
See Print Dropdown on Detail Views.
Step 3: Import the Report Views Module
# myapp/views/__init__.py
from .report_views import *
from .invoice_views import *
Step 4: Ensure URL Discovery
Your app's urls.py must include *dynamic_urls (most Enezaframework apps already do):
# myapp/urls.py
from django.urls import path
from enezaframework.utils import get_registered_urls
app_name = "myapp"
dynamic_urls = get_registered_urls(app_name)
urlpatterns = [
*dynamic_urls,
]
Complete Example
Model Layer
# myapp/models.py
from django.db import models
from enezaframework.models import EnezaDocument, EnezaDocumentLines
class Invoice(EnezaDocument):
invoice_number = models.CharField(
max_length=100,
verbose_name="Invoice Number",
)
customer = models.CharField(
max_length=200,
verbose_name="Customer",
)
date = models.DateField(
verbose_name="Date",
)
due_date = models.DateField(
verbose_name="Due Date",
)
status = models.CharField(
max_length=30,
verbose_name="Status",
)
sequence_field_name = "invoice_number"
document_name = "SALES_INVOICE"
class Meta:
verbose_name = "Invoice"
verbose_name_plural = "Invoices"
class InvoiceLine(EnezaDocumentLines):
invoice = models.ForeignKey(
Invoice,
on_delete=models.CASCADE,
related_name="lines",
verbose_name="Invoice",
)
item_code = models.CharField(
max_length=100,
verbose_name="Item Code",
)
description = models.CharField(
max_length=200,
verbose_name="Description",
)
quantity = models.IntegerField(
verbose_name="Quantity",
)
unit_price = models.DecimalField(
max_digits=10,
decimal_places=2,
verbose_name="Unit Price",
)
line_total = models.DecimalField(
max_digits=10,
decimal_places=2,
verbose_name="Line Total",
)
class Meta:
verbose_name = "Invoice Line"
verbose_name_plural = "Invoice Lines"
Report View
# myapp/views/report_views.py
from enezareporting.report_base import DocumentReportView
from enezaframework.utils import auto_url_register
from myapp.models import Invoice
@auto_url_register
class InvoiceReportView(DocumentReportView):
model = Invoice
report_name = "invoice"
header_model = Invoice
header_fields = ["invoice_number", "customer", "date", "due_date", "status"]
lines_related_name = "lines"
line_columns = ["item_code", "description", "quantity", "unit_price", "line_total"]
line_total_fields = ["line_total", "quantity"]
Multiple Reports for One Model
# myapp/views/report_views.py
from enezareporting.report_base import DocumentReportView
from enezaframework.utils import auto_url_register
from myapp.models import Invoice
@auto_url_register
class InvoiceSummaryReportView(DocumentReportView):
model = Invoice
report_name = "invoice_summary"
header_model = Invoice
header_fields = ["invoice_number", "customer", "date", "status"]
lines_related_name = "lines"
line_columns = ["item_code", "description", "line_total"]
line_total_fields = ["line_total"]
@auto_url_register
class InvoiceTaxReportView(DocumentReportView):
model = Invoice
report_name = "invoice_tax"
header_model = Invoice
header_fields = ["invoice_number", "customer", "date", "status"]
lines_related_name = "lines"
line_columns = ["item_code", "description", "unit_price", "tax_amount", "line_total"]
line_total_fields = ["tax_amount", "line_total"]
Detail View with Multiple Print Actions
# myapp/views/invoice_views.py
from enezaframework.views import BaseSourceDocumentDetailView
from enezaframework.utils import auto_url_register
from myapp.models import Invoice
@auto_url_register
class InvoiceDetailView(BaseSourceDocumentDetailView):
model = Invoice
print_actions = [
{"name": "Invoice Summary", "icon": "printer", "url": "invoice_summary_report"},
{"name": "Invoice Detail", "icon": "printer", "url": "invoice_detail_report"},
{"name": "Tax Report", "icon": "printer", "url": "invoice_tax_report"},
]