Document Report Page View

Developer guide for the Enezaframework reporting engine. Part of the Add Report series.

Table of Contents

  1. Overview
  2. Architecture
  3. Data Flow
  4. Quick Start
  5. Complete Example
  6. Related Guides

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:

  1. Subclass DocumentReportView and set model, header_model, header_fields, line_columns, line_total_fields.
  2. Optionally set report_name to control the URL name (one model can power many reports).
  3. Decorate with @auto_url_register.
  4. The URL is auto-registered as {app_name}:{report_name}_report, accepting <str:pk>.
  5. Set print_actions on the detail view — these appear in a Print dropdown (distinct from Actions).
  6. The user clicks a Print option and the report renders immediately for that document in #detail-container via 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/&lt;str:pk&gt;/"]
    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 + totals

Quick Start

Step 1: Define the Report View (Single Class)

python
# 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

python
# 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

python
# 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):

python
# 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

python
# 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

python
# 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

python
# 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

python
# 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"},
    ]