Примеры отчетов

Пример XLS-отчета

Данный пример основан на демо-приложении Library. Следуйте инструкциям, чтобы настроить проект, а затем создайте объекты и экраны.

В этом примере мы сгенерируем отчет для автора книги. При наличии автора в отчете будут указаны его книги, издатель каждой книги, в каком отделе библиотеки хранилась книга и сколько книг хранилось в каждом отделе. Готовый отчет выглядит следующим образом:

sample1 result
Figure 1. Книги по авторам
  1. Структура данных отчета.

    sample1 structure
    Figure 2. Структура данных отчета

    Рассмотрим полосы отчета.

    • Полоса header – заголовок отчета. Содержит набор данных с Groovy-скриптом, выводящим значения внешних параметров отчета:

      [['authorName' : (params['author'].firstName + ' ' + params['author'].lastName)]]
    • Полоса book выводит список книг путем выполнения следующего SQL-запроса:

      select b.name as book_name, b.id as book_id
      from JMXRPR_BOOK b
          join JMXRPR_AUTHORS_BOOKS ba on ba.book_id = b.id
          join JMXRPR_AUTHOR a on a.id = ba.author_id
      where a.id = ${author}

      В данном запросе используется внешний параметр отчета – author. Параметр имеет тип Entity, однако в SQL-запросах его можно напрямую сравнивать с полями-идентификаторами сущностей, преобразование будет выполнено автоматически.

    • Вложенная в book полоса publisher выводит издателей книги путем выполнения следующего SQL-запроса:

      select p.name as publisher, bp.year_, p.id as publisher_id
      from JMXRPR_BOOK_PUBLICATION bp
          join JMXRPR_PUBLISHER p on p.id = bp.publisher_id
      where bp.book_id = ${book.book_id}

      В данном запросе в качестве параметра используется поле родительской полосы – book_id. Таким образом осуществляется связь между родительской и дочерней полосами.

    • Вложенная в publication полоса publisher выводит издания книги путем выполнения следующего SQL-запроса:

      select ld.name as department, sum(bi.book_count) as amount
      from JMXRPR_BOOK_INSTANCE bi
          join JMXRPR_BOOK_PUBLICATION bp on bp.id = bi.book_publication_id
          join JMXRPR_LIBRARY_DEPARTMENT ld on ld.id = bi.library_department_id
      where bp.publisher_id = ${publisher.publisher_id} and bp.book_id = ${book.book_id}
      group by ld.name

      В данном запросе в качестве параметров используются поля обоих родительских полос – book_id и publisher_id.

  2. Параметры отчета.

    На вкладке Parameters and Formats объявлен один внешний параметр отчета – Author:

    sample1 param
    Figure 3. Параметры отчета

    Этот параметр запрашивается у пользователя при запуске отчета. Выбор автора производится через экран Author.browse, имеющийся в приложении.

  3. Шаблоны отчета.

    На вкладке Templates определен один шаблон формата XLS, загруженный из файла BooksByAuthor.xls

    sample1 template
    Figure 4. Шаблоны отчета
  4. Локализация названия отчета.

    На вкладке Localization задано название отчета для русской локали:

    ru = Книги по автору

Вызовите отчет на исполнение из общего списка в экране ReportsRun Reports.

Пример перекрестного отчета

Данный пример основан на демо-приложении Library. Следуйте инструкциям, чтобы настроить проект, а затем создайте объекты и экраны.

В этом примере мы создадим перекрестный отчет для отделов библиотеки, чтобы показать, сколько книг каждый отдел приобретал каждый месяц. Отчет расширяется как по вертикали, так и по горизонтали и агрегирует количества книг по каждому отделу и каждому месяцу:

crosstab result
Figure 5. Готовый перекрестный отчет

Для создания отчета выберите ориентацию полосы Crosstab на вкладке Report structure редактора отчетов. При выборе этой ориентации к полосе автоматически добавляются три набора данных:

  1. <band_name>_dynamic_header – данные из этого набора заполняют отчет значениями слева направо, то есть он ведет себя, как вертикальная полоса с заголовками столбцов матрицы.

  2. <band_name>_master_data – данные из этого набора заполняют отчет значениями сверху внизу, то есть он ведет себя, как горизонтальная полоса с заголовками строк матрицы.

  3. <band_name> – набор данных, названный так же, как полоса, в которой он создан. Этот набор содержит данные для заполнения ячеек матрицы.

Для этих наборов данных вы можете выбрать любой из доступных типов: SQL, JPQL, Groovy, и т.д.

Создадим перекрестный отчет для сущности BookInstance из демо-приложения Library со следующей структурой:

crosstab structure
Figure 6. Перекрестный отчет
  1. Структура данных отчета. Есть три набора данных:

    • bi_dynamic_header возвращает список названий месяцев:

      bi_dynamic_header dataset
      import java.text.DateFormatSymbols
      
      List result = new ArrayList()
      DateFormatSymbols dateFormatSymbols = DateFormatSymbols.getInstance(Locale.ENGLISH)
      for (i in 0..dateFormatSymbols.months.length - 1) {
          result.add(["header_id" : i + 1, "month_name" : dateFormatSymbols.months[i]])
      }
      return result
    • bi_master_data возвращает имена и идентификаторы отделов библиотеки, выбранных пользователем в качестве внешнего параметра отчета:

      bi_master_data dataset
      select name as name, id as department_id
      from JMXRPR_LIBRARY_DEPARTMENT
      where id in (${selected_departments})
    • Набор данных bi генерирует данные для заполнения ячеек матрицы, то есть сумму книг за конкретный месяц и отдел. Он использует bi_master_data@department_id (идентификатор покупателя) как вертикальную координату ячейки и bi_dynamic_header@header_id (название месяца) как горизонтальную координату, а затем заполняет ячейку суммой значений amount.

      В примере ниже мы использовали два дополнительных внешних параметра: start_date и end_date, которые определяют временной диапазон заказов. Мы рекомендуем использовать перекрестную валидацию значений введенных параметров, чтобы избежать ошибок, вызванных неправильным диапазоном дат.

      bi dataset
      select bi.library_department_id as bi_master_data@department_id,
             month(bi.created_date) as bi_dynamic_header@header_id,
             sum(bi.book_count) as "amount"
      from JMXRPR_BOOK_INSTANCE bi
      where bi.created_date >= ${start_date} and bi.created_date<= ${end_date}
      and bi.library_department_id in (${bi_master_data@department_id})
      and month(bi.created_date) in (${bi_dynamic_header@header_id})
      group by bi.library_department_id,month(bi.created_date)
      order by bi.library_department_id,month(bi.created_date)
  2. Параметры отчета.

    На вкладке Parameters and Formats объявлены внешние параметры отчета – selected_departments, start_date, end_date:

    crosstab external params
    Figure 7. Параметры отчета

    Эти параметры запрашиваются у пользователя при запуске отчета. Выбор отдела производится через экран jmxrpr_LibraryDepartment.browse, имеющийся в приложении.

  3. Шаблон отчета.

    Теперь создадим шаблон отчета, используя Microsoft Office или LibreOffice.

    DepartmentBooks.xls – это пример шаблона, который выводит список Departments по вертикали и Books для каждого отдела по горизонтали, сгруппированный по месяцам создания книг.

    Данный шаблон отчета содержит именованные области для всех трех наборов данных перекрестной полосы, а также именованную область для заголовка столбца: <band_name>_header. В нашем случае это bi_header.

Запустить отчет можно из общего браузера на экране ReportsRun Reports.

Пример отчета JasperReports

Данный пример основан на демо-приложении Library. Следуйте инструкциям, чтобы настроить проект, а затем создайте объекты и экраны.

Создадим JRXML-шаблон, который выводит список публикаций книг, доступных в выбранном отделе:

sample jasper result
Figure 8. Готовый отчет
  1. Структура данных отчета.

    sample jasper structure
    Figure 9. Структура данных отчета

    Рассмотрим полосы отчета.

    • Header – заголовок отчета. Она содержит набор данных с Groovy-скриптом, выводящим значение внешнего параметра отчета:

      [['library_department_name' : params['library_department'].name]]
    • Полоса Data выводит список книг в выбранном отделе путем выполнения следующего Groovy-скрипта:

      import reports.ex2.entity.LiteratureType
      
      def result = []
      
      def ltList = dataManager.load(LiteratureType).all().list();
      ltList.each(lt->{
          def count = dataManager.loadValue("select sum(bi.bookCount) from jmxrpr_BookInstance bi where bi.libraryDepartment = :department and bi.bookPublication.book.literatureType = :lt ", Long)
                  .parameter("department", params['library_department']).parameter("lt", lt)
                  .one();
      
          def refCount = dataManager.loadValue("select sum(bi.bookCount) from jmxrpr_BookInstance bi where bi.libraryDepartment = :department and bi.bookPublication.book.literatureType = :lt and bi.isReference = true", Long)
                  .parameter("department", params['library_department']).parameter("lt", lt)
                  .one();
                  
          result.add(['literature_type_name': lt.name,
                  'books_instances_amount': count,
                  'reference_books_instances_amount': refCount])
      });
      return result;

      В данном запросе используется внешний параметр отчета – library_department. Параметр имеет тип Entity, однако его можно напрямую сравнивать с полями-идентификаторами сущностей, преобразование будет выполнено автоматически.

  2. Параметры отчета.

    На вкладке Parameters and Formats объявлен один внешний параметр отчета – Department:

    sample jasper paramters
    Figure 10. Параметры отчета

    Этот параметр запрашивается у пользователя при запуске отчета. Выбор отдела производится через экран jmxrpr_LibraryDepartment.browse, имеющийся в приложении.

  3. Шаблон отчета.

    Создайте новый JRXML-файл (или скачайте BookAvailability.jrxml) со следующим содержимым:

    BookAvailability.jrxml
    <?xml version="1.0" encoding="UTF-8"?>
    <!-- Created with Jaspersoft Studio version 6.4.0.final using JasperReports Library version 6.4.1 -->
    <jasperReport xmlns="http://jasperreports.sourceforge.net/jasperreports"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://jasperreports.sourceforge.net/jasperreports http://jasperreports.sourceforge.net/xsd/jasperreport.xsd" name="books" pageWidth="595" pageHeight="842" whenNoDataType="AllSectionsNoDetail" columnWidth="535" leftMargin="20" rightMargin="20" topMargin="20" bottomMargin="20">
        <property name="template.engine" value="tabular_template"/>
        <property name="com.jaspersoft.studio.data.defaultdataadapter" value="One Empty Record"/>
        <style name="Table_TH" mode="Opaque" backcolor="#066990">
            <box>
                <topPen lineWidth="0.5" lineColor="#000000"/>
                <bottomPen lineWidth="0.5" lineColor="#000000"/>
            </box>
        </style>
        <style name="Table_CH" mode="Opaque" forecolor="#FFFFFF" backcolor="#06618F" hTextAlign="Center" fontSize="12">
            <box>
                <topPen lineWidth="0.5" lineColor="#000000"/>
                <bottomPen lineWidth="0.5" lineColor="#000000"/>
            </box>
        </style>
        <style name="Table_TD" mode="Opaque" backcolor="#FFFFFF" hTextAlign="Center">
            <box>
                <topPen lineWidth="0.5" lineColor="#000000"/>
                <bottomPen lineWidth="0.5" lineColor="#000000"/>
            </box>
        </style>
        <subDataset name="Data">
            <field name="literature_type_name" class="java.lang.String"/>
            <field name="books_instances_amount" class="java.lang.Long"/>
            <field name="reference_books_instances_amount" class="java.lang.Long"/>
        </subDataset>
        <field name="library_department_name" class="java.lang.String"/>
        <title>
            <band height="72">
                <frame>
                    <reportElement mode="Opaque" x="-20" y="-20" width="595" height="92" backcolor="#006699"/>
                    <staticText>
                        <reportElement x="20" y="10" width="555" height="30" forecolor="#FFFFFF"/>
                        <textElement textAlignment="Center">
                            <font size="20" isBold="true"/>
                        </textElement>
                        <text><![CDATA[Book availability in department]]></text>
                    </staticText>
                    <textField>
                        <reportElement x="20" y="50" width="555" height="30" forecolor="#FFFFFF"/>
                        <box>
                            <pen lineWidth="1.0" lineColor="#FFFFFF"/>
                            <topPen lineWidth="0.0" lineStyle="Solid" lineColor="#000000"/>
                            <leftPen lineWidth="0.0" lineStyle="Solid" lineColor="#000000"/>
                            <bottomPen lineWidth="0.0" lineStyle="Solid" lineColor="#000000"/>
                            <rightPen lineWidth="0.0" lineStyle="Solid" lineColor="#000000"/>
                        </box>
                        <textElement textAlignment="Center" verticalAlignment="Middle">
                            <font fontName="SansSerif" size="20" isBold="true"/>
                        </textElement>
                        <textFieldExpression><![CDATA[$F{library_department_name}]]></textFieldExpression>
                    </textField>
                </frame>
            </band>
        </title>
        <detail>
            <band height="204">
                <componentElement>
                    <reportElement x="0" y="4" width="555" height="200" forecolor="#FFFFFF">
                        <property name="com.jaspersoft.studio.layout" value="com.jaspersoft.studio.editor.layout.VerticalRowLayout"/>
                        <property name="com.jaspersoft.studio.table.style.table_header" value="Table_TH"/>
                        <property name="com.jaspersoft.studio.table.style.column_header" value="Table_CH"/>
                        <property name="com.jaspersoft.studio.table.style.detail" value="Table_TD"/>
                        <property name="net.sf.jasperreports.export.headertoolbar.table.name" value=""/>
                        <property name="com.jaspersoft.studio.components.autoresize.proportional" value="true"/>
                    </reportElement>
                    <jr:table xmlns:jr="http://jasperreports.sourceforge.net/jasperreports/components" xsi:schemaLocation="http://jasperreports.sourceforge.net/jasperreports/components http://jasperreports.sourceforge.net/xsd/components.xsd">
                        <datasetRun subDataset="Data">
                            <dataSourceExpression><![CDATA[$P{REPORTING}.dataset("Data")]]></dataSourceExpression>
                        </datasetRun>
                        <jr:column width="188">
                            <jr:columnHeader style="Table_CH" height="30">
                                <staticText>
                                    <reportElement x="0" y="0" width="188" height="30" forecolor="#FFFFFF"/>
                                    <box>
                                        <pen lineColor="#FFFFFF"/>
                                    </box>
                                    <textElement textAlignment="Center" verticalAlignment="Middle">
                                        <font fontName="SansSerif" size="12" isBold="true"/>
                                    </textElement>
                                    <text><![CDATA[Literature Type]]></text>
                                </staticText>
                            </jr:columnHeader>
                            <jr:detailCell style="Table_TD" height="30">
                                <textField>
                                    <reportElement x="0" y="0" width="188" height="30"/>
                                    <textElement textAlignment="Center" verticalAlignment="Middle">
                                        <font fontName="SansSerif" size="12"/>
                                    </textElement>
                                    <textFieldExpression><![CDATA[$F{literature_type_name}]]></textFieldExpression>
                                </textField>
                            </jr:detailCell>
                        </jr:column>
                        <jr:column width="186">
                            <jr:columnHeader style="Table_CH" height="30">
                                <staticText>
                                    <reportElement x="0" y="0" width="186" height="30" forecolor="#FFFFFF"/>
                                    <textElement textAlignment="Center" verticalAlignment="Middle">
                                        <font fontName="SansSerif" size="12" isBold="true"/>
                                    </textElement>
                                    <text><![CDATA[Book Amount]]></text>
                                </staticText>
                            </jr:columnHeader>
                            <jr:detailCell style="Table_TD" height="30">
                                <textField>
                                    <reportElement x="0" y="0" width="186" height="30"/>
                                    <textElement textAlignment="Center" verticalAlignment="Middle">
                                        <font size="12"/>
                                    </textElement>
                                    <textFieldExpression><![CDATA[$F{books_instances_amount}]]></textFieldExpression>
                                </textField>
                            </jr:detailCell>
                        </jr:column>
                        <jr:column width="181">
                            <jr:columnHeader style="Table_CH" height="30">
                                <staticText>
                                    <reportElement x="0" y="0" width="181" height="30" forecolor="#FFFFFF"/>
                                    <textElement textAlignment="Center" verticalAlignment="Middle">
                                        <font fontName="SansSerif" size="12" isBold="true"/>
                                    </textElement>
                                    <text><![CDATA[Reference Book Amount]]></text>
                                </staticText>
                            </jr:columnHeader>
                            <jr:detailCell style="Table_TD" height="30">
                                <textField isBlankWhenNull="false">
                                    <reportElement x="0" y="0" width="181" height="30" forecolor="#000000"/>
                                    <textElement textAlignment="Center" verticalAlignment="Middle">
                                        <font size="12"/>
                                    </textElement>
                                    <textFieldExpression><![CDATA[$F{reference_books_instances_amount}]]></textFieldExpression>
                                </textField>
                            </jr:detailCell>
                        </jr:column>
                    </jr:table>
                </componentElement>
            </band>
        </detail>
        <pageFooter>
            <band height="17">
                <textField>
                    <reportElement mode="Opaque" x="0" y="4" width="515" height="13" backcolor="#E6E6E6"/>
                    <textElement textAlignment="Right"/>
                    <textFieldExpression><![CDATA["Page "+$V{PAGE_NUMBER}+" of"]]></textFieldExpression>
                </textField>
                <textField evaluationTime="Report">
                    <reportElement mode="Opaque" x="515" y="4" width="40" height="13" backcolor="#E6E6E6"/>
                    <textFieldExpression><![CDATA[" " + $V{PAGE_NUMBER}]]></textFieldExpression>
                </textField>
                <textField pattern="M/d/yy">
                    <reportElement x="0" y="4" width="280" height="13"/>
                    <textFieldExpression><![CDATA[new java.util.Date()]]></textFieldExpression>
                </textField>
            </band>
        </pageFooter>
    </jasperReport>

    Таблица в этом шаблоне привязана к дочернему источнику данных subDataset. Элемент title обращается к данным полосы Header напрямую. Вы можете заранее посмотреть, как будет выглядеть отчет, открыв шаблон в визуальном редакторе JasperReports.

    Загрузите новый шаблон в приложение, выбрав любой тип вывода, и сделайте его шаблоном по умолчанию:

    sample jasper template
    Figure 11. Редактор шаблона

Запустить отчет можно из общего браузера на экране ReportsRun Reports.

Пример отчета HTML/PDF

Данный пример основан на демо-приложении Library. Следуйте инструкциям, чтобы настроить проект, а затем создайте объекты и экраны.

Создадим отчет кратких описаний книг с альбомной ориентацией страниц, нумерацией, а также фиксированными заголовком и подвалом на каждой странице, которые мы настроим через правила и свойства CSS. Формат вывода отчета – HTML с конвертацией в PDF:

example html result
Figure 12. Готовый отчет
  1. Структура отчета

    Создайте простой отчет без параметров. Запрос JPQL возвращает список всех книг с их локальными атрибутами: name and summary.

    example html structure
    Figure 13. Структура данных отчета
  2. Шаблон отчета.

    Теперь создайте файл шаблона. Определите в нем блоки заголовка и подвала, которые должны выводиться на каждой странице итогового документа PDF. Также используйте свойство CSS page-break-before: always, которое будет создавать разрыв страницы перед каждым новым блоком информации о книге.

    Используйте теги FreeMarker для вставки данных в тело отчета. См. полную документацию по FreeMarker.

    <body>
        <h1>Books report</h1>
    
        <!-- Custom HTML header -->
        <div class="header">
            Library book summaries
        </div>
    
        <!-- Custom HTML footer -->
        <div class="footer">
            Address: William Road
        </div>
    
        <#assign books=Root.bands.Books />
    
        <#list books as book>
            <div class="custom-page-start" style="page-break-before: always;">
                <h2>Book</h2>
    
                <p>Name: ${book.fields.title}</p>
                <p>Summary: ${book.fields.summary}</p>
            </div>
        </#list>
    </body>
  3. Правила CSS

    Используйте следующий код CSS для разметки страницы PDF:

    div.header {
        display: block;
        text-align: center;
        position: running(header);
        width: 100%;
    }
    
    div.footer {
        display: block;
        text-align: center;
        position: running(footer);
        width: 100%;
    }

    Также используйте следующий CSS-код для настройки отобажения страницы в формате PDF и отступов для основного содержимого отчета, чтобы избежать наложения с заголовком и подвалом:

    body {
        font: 12pt Georgia, "Times New Roman", Times, serif;
        line-height: 1.3;
        padding-top: 50px;
    }
    
    @page {
        /* switch to landscape */
        size: landscape;
        /* set page margins */
        margin: 0.5cm;
    
        @top-center {
            content: element(header);
        }
    
        @bottom-center {
            content: element(footer);
        }
    
        @bottom-right {
            content: counter(page) " of "counter(pages);
        }
    }
    
    .custom-page-start {
        margin-top: 50px;
    }

    В итоге получился файл BookSummary.html со следующим содержанием:

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml">
    
    <head>
        <title>Invoice</title>
        <style type="text/css">
            body {
                font: 12pt Georgia, "Times New Roman", Times, serif;
                line-height: 1.3;
                padding-top: 50px;
            }
    
    
            div.header {
                display: block;
                text-align: center;
                position: running(header);
                width: 100%;
            }
    
            div.footer {
                display: block;
                text-align: center;
                position: running(footer);
                width: 100%;
            }
    
            @page {
                /* switch to landscape */
                size: landscape;
                /* set page margins */
                margin: 0.5cm;
    
                @top-center {
                    content: element(header);
                }
    
                @bottom-center {
                    content: element(footer);
                }
    
                @bottom-right {
                    content: counter(page) " of "counter(pages);
                }
            }
    
            .custom-page-start {
                margin-top: 50px;
            }
    
        </style>
    </head>
    
    
    <body>
        <h1>Books report</h1>
    
        <!-- Custom HTML header -->
        <div class="header">
            Library book summaries
        </div>
    
        <!-- Custom HTML footer -->
        <div class="footer">
            Address: William Road
        </div>
    
        <#assign books=Root.bands.Books />
    
        <#list books as book>
            <div class="custom-page-start" style="page-break-before: always;">
                <h2>Book</h2>
    
                <p>Name: ${book.fields.title}</p>
                <p>Summary: ${book.fields.summary}</p>
            </div>
        </#list>
    </body>
    
    </html>
  4. Создавая шаблон отчета, мы выберем тип шаблона Freemarker:

    example html template
    Figure 14. Редактор шаблона

    Запустить отчет можно из общего браузера на экране ReportsRun Reports.

HTML отчет с шаблонизатором Groovy

Данный пример основан на демо-приложении Library. Следуйте инструкциям, чтобы настроить проект, а затем создайте объекты и экраны.

Давайте создадим отчет, который выводит список книжных публикаций для выбранного города. Формат вывода по умолчанию – HTML:

html groovy result
Figure 15. Готовый отчет
  1. Создайте отчет с набором данных JPQL:

    html groovy structure
    Figure 16. Структура данных отчета

    Полоса BookPublications выводит список публикаций путем выполнения следующего JPQL запроса:

    BookPublications dataset
    select
    b.name as "book",
    p.name as "publisher"
    from jmxrpr_BookPublication bp
    left join bp.book b
    left join bp.publisher p
     where bp.town.id = ${town}

    В запросе используется внешний параметр отчета – town. Параметр имеет тип Entity, однако его можно напрямую сравнивать с полями-идентификаторами сущностей, преобразование будет выполнено автоматически.

  2. Задайте параметр отчета:

    На вкладке Parameters and Formats объявлен один внешний параметр отчета – Town:

    html groovy parameter
    Figure 17. Параметр отчета

    Этот параметр запрашивается у пользователя при запуске отчета. Выбор города производится через экран jmxrpr_Town.browse, имеющийся в приложении.

  3. Создайте шаблон отчета:

    Создайте новый HTML-файл (или скачайте PublicationByTown.html) со следующим содержимым:

    PublicationsTemplate
    <?xml version="1.0" encoding="utf-8"?>
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ru">
    
    <head>
        <title> Publications by town </title>
        <style type="text/css">
            body {
                font: 12pt Georgia, "Times New Roman", Times, serif;
                line-height: 1.3;
                padding-top: 30px;
            }
    
            tbody tr {
                height: 40px;
                min-height: 20px
            }
        </style>
    </head>
    
    <body>
        <h1>Publications, published in <% out << "${Root.fields.town.name}" %>
        </h1>
        <% def bookPublications=Root.bands.BookPublications.fields %>
            <table class="report-table" border="1" cellspacing="2">
                <thead>
                    <tr>
                        <th>Book</th>
                        <th>Publisher</th>
                    </tr>
                </thead>
                <tbody>
                    <% bookPublications.title.eachWithIndex{ elem, index ->
                        out << "<tr><td> ${bookPublications.book[index]} </td><td> ${bookPublications.publisher[index]} </td></tr>"
                            } %>
                </tbody>
            </table>
    </body>
    
    </html>

    Для формирования заголовка отчета используется значение входного параметра: ${Root.fields.town.name}.

    Ниже определена переменная bookPublications:

    <% def bookPublications=Root.bands.BookPublications.fields %>

    Эта переменная используется в теле таблицы для вывода полей отчета.

    <% bookPublications.title.eachWithIndex{ elem, index ->
        out << "<tr><td> ${bookPublications.book[index]} </td><td> ${bookPublications.publisher[index]} </td></tr>"
            } %>

    Загрузите новый шаблон в приложение, выбрав формат вывода HTML, установите переключатель Template type в значение Groovy template и сделайте его шаблоном по умолчанию:

    html groovy template
    Figure 18. Редактор шаблона отчета

Запустить отчет можно из общего браузера на экране ReportsRun Reports.