Generate Master-Detail Reports

Overview

Master-detail reports display hierarchical data where each master record (parent) has associated detail records (children). GroupDocs.Assembly for Python via .NET supports master-detail reports through nested foreach data bands, allowing you to display parent records with their related child records.

The main classes involved are:

Here are the steps to generate master-detail reports:

  • Define data structures with parent-child relationships
  • Create a template with nested foreach data bands
  • Outer foreach iterates over master records
  • Inner foreach iterates over detail records for each master
  • Assemble the document with your hierarchical data
Note
Nested foreach data bands allow you to create complex hierarchical reports. The outer band processes master records, while inner bands process related detail records.

Define Master-Detail Data Structure

In Python, no classes are needed: a master record is a dict, and one of its keys holds the list of its detail records, each a dict too. Every order below has an Items list:

order = {
    "OrderId": 1001,
    "OrderDate": datetime(2015, 1, 14),
    "CustomerName": "Quayside Bistro",
    "Items": [
        {"ProductName": "Harbor Blend whole bean coffee", "Quantity": 2, "Price": Decimal("18.40")},
    ],
}

The order date is a datetime, so the template can format it; the prices are Decimal, so sums keep their cents.

Generate Master-Detail Report

Create a master-detail report from hierarchical data:

from datetime import datetime
from decimal import Decimal

from groupdocs.assembly import DataSourceInfo, DocumentAssembler

# Create sample data with master-detail relationship
orders = [
    {
        "OrderId": 1001,
        "OrderDate": datetime(2015, 1, 14),
        "CustomerName": "Quayside Bistro",
        "Items": [
            {"ProductName": "Harbor Blend whole bean coffee", "Quantity": 2, "Price": Decimal("18.40")},
            {"ProductName": "Oat and honey biscuits", "Quantity": 10, "Price": Decimal("3.20")},
        ],
    },
    {
        "OrderId": 1002,
        "OrderDate": datetime(2015, 1, 16),
        "CustomerName": "Lantern Street Café",
        "Items": [
            {"ProductName": "Lighthouse Espresso beans", "Quantity": 1, "Price": Decimal("21.75")},
        ],
    },
]

# Assemble document
assembler = DocumentAssembler()
assembler.assemble_document("order-details.docx", "order-details-report.docx",
                            DataSourceInfo(orders, "orders"))

print("Master-detail report generated successfully.")

The following sample file is used in this example: order-details.docx

Binary file (DOCX, 7 KB)

Download full output

Template Structure for Master-Detail

In your template (order-details.docx), use nested foreach data bands:

<<foreach [in orders]>>Order #<<[OrderId]>>
Date: <<[OrderDate]:"yyyy-MM-dd">>
Customer: <<[CustomerName]>>

Items:
<<foreach [in Items]>>  - <<[ProductName]>> (Qty: <<[Quantity]>>, Price: <<[Price]:"C">>)
<</foreach>>
Total: <<[Items.Sum(i => i.Price * i.Quantity)]:"C">>

<</foreach>>

Each band opens in the first paragraph it repeats and closes at the start of the paragraph after its last one. After assembly, you’ll get:

Order #1001
Date: 2015-01-14
Customer: Quayside Bistro

Items:
  - Harbor Blend whole bean coffee (Qty: 2, Price: $18.40)
  - Oat and honey biscuits (Qty: 10, Price: $3.20)

Total: $68.80

Order #1002
Date: 2015-01-16
Customer: Lantern Street Café

Items:
  - Lighthouse Espresso beans (Qty: 1, Price: $21.75)

Total: $21.75

Master-Detail in Tables

You can also create master-detail reports in table format:

from decimal import Decimal

from groupdocs.assembly import DataSourceInfo, DocumentAssembler

orders = [
    {
        "OrderId": 1001,
        "CustomerName": "Quayside Bistro",
        "Items": [
            {"ProductName": "Harbor Blend whole bean coffee", "Quantity": 2, "Price": Decimal("18.40")},
            {"ProductName": "Oat and honey biscuits", "Quantity": 10, "Price": Decimal("3.20")},
        ],
    },
]

assembler = DocumentAssembler()
assembler.assemble_document("order-items-table.docx", "order-items-table-report.docx",
                            DataSourceInfo(orders, "orders"))

print("Master-detail table generated successfully.")

The following sample file is used in this example: order-items-table.docx

Binary file (DOCX, 8 KB)

Download full output

Template table structure:

Order IDCustomerProductQuantityPrice
<<foreach [in orders]>><<[OrderId]>><<[CustomerName]>>
<<foreach [in Items]>><<[ProductName]>><<[Quantity]>><<[Price]:"C">><</foreach>><</foreach>>

The outer band spans both rows: it opens in the first cell of the order row and closes in the last cell of the item row. The inner band is the item row alone, so the table gets one order row followed by one row per item.

Using Aggregates in Master-Detail

Calculate totals and aggregates for each master record:

from decimal import Decimal

from groupdocs.assembly import DataSourceInfo, DocumentAssembler

orders = [
    {
        "OrderId": 1001,
        "CustomerName": "Quayside Bistro",
        "Items": [
            {"ProductName": "Harbor Blend whole bean coffee", "Quantity": 2, "Price": Decimal("18.40")},
            {"ProductName": "Oat and honey biscuits", "Quantity": 10, "Price": Decimal("3.20")},
        ],
    },
]

assembler = DocumentAssembler()
assembler.assemble_document("order-items-subtotals.docx", "order-items-subtotals-report.docx",
                            DataSourceInfo(orders, "orders"))

print("Master-detail report with aggregates generated successfully.")

The following sample file is used in this example: order-items-subtotals.docx

Binary file (DOCX, 7 KB)

Download full output

Template with aggregates:

<<foreach [in orders]>>Order #<<[OrderId]>> - <<[CustomerName]>>

Items:
<<foreach [in Items]>>  <<[ProductName]>> - <<[Quantity]>> x <<[Price]:"C">> = <<[Quantity * Price]:"C">>
<</foreach>>
Subtotal: <<[Items.Sum(i => i.Price * i.Quantity)]:"C">>
Item Count: <<[Items.Count()]>>

<</foreach>>
Warning
When using nested foreach data bands, ensure proper closing tags. Each opening <<foreach>> must have a corresponding closing <</foreach>>, and an inner band must close before the band around it.

Advanced Usage Topics

To learn more about complex hierarchical structures, multiple levels of nesting, and advanced master-detail patterns, please refer to the advanced usage section.

More resources

GitHub Examples

You may easily run the code above and see the feature in action in our GitHub examples:

Free Online Apps

Along with the full-featured library, we provide simple but powerful free online apps.

To assemble documents from templates and data sources, you can use the online apps from the GroupDocs.Assembly App Product Family.

Close
Loading

Analyzing your prompt, please hold on...

An error occurred while retrieving the results. Please refresh the page and try again.