В этой статье я хочу вам рассказать о такой мощной возможности FastReport, как “многоуровневые отчёты” - можно сравнить их структуру с деревом “ствол, ветки потолще, из каждой растут потоньше, и так далее - вплоть до листиков” или со структурой предприятия “Отделы, подотделы, сотрудники”. Часто их называют “master-detail” или “главный-подчинённый” - и делаются такие отчёты из нескольких таблиц. В одной таблице список главных сущностей, в другой, связанной с первой, список подчинённых сущностей со ссылкой на первую - какой конкретно сущности из первой подчинена конкретная сущность из подчинённой. И так далее.
FastReport поддерживает вложенность до 6 уровней (можно и больше, используя объект "Вложенный отчет", но об этом позже). В реальных приложениях редко приходится печатать отчеты с большой вложенностью данных; как правило, ограничиваются 1-3 уровнями.
Рассмотрим создание на примере двухуровневого отчета. Он будет содержать данные из таблиц Customer и Orders. Первая таблица – это список клиентов, вторая – список заказов, сделанных клиентами. Таблицы содержат данные следующего вида:
Customer:
CustNo Company
1221 Kauai Dive Shoppe
1231 Unisco
1351 Sight Diver
Orders:
OrderNo CustNo SaleDate
1003 1351 12.04.1988
1023 1221 01.07.1988
1052 1351 06.01.1989
1055 1351 04.02.1989
1060 1231 28.02.1989
1123 1221 24.08.1993
Как видно, вторая таблица содержит список всех заказов, сделанных всеми компаниями. Чтобы получить список заказов, сделанных конкретной компанией, из таблицы следует отобрать записи, у которых поле CustNo = номеру выбранной компании. Отчет, построенный на этих данных, будет выглядеть следующим образом:
1221 Kauai Dive Shoppe
1023 01.07.1988
1123 24.08.1993
1231 Unisco
1060 28.02.1989
1351 Sight Diver
1003 12.04.1988
1052 06.01.1989
1055 04.02.1989
Приступим к созданию отчета. Создадим новый проект в Delphi, на форму положим два компонента TTable, компонент TDataSource, два компонента TfrxDBDataSet и один TfrxReport.
Настроим компоненты следующим образом:
Table1: DatabaseName = 'DBDEMOS' TableName = 'Customer.db' Table2: DatabaseName = 'DBDEMOS' TableName = 'Orders.db' DataSource1: DataSet = Table1 frxDBDataSet1: DataSet = Table1 UserName = 'Customers' frxDBDataSet2: DataSet = Table2 UserName = 'Orders'
В дизайнере отчета подключим наши источники данных в окне "Отчет|Данные…".
Добавим на страницу бэнды "Данные 1 уровня"(master) и "Данные 2 уровня"(detail). И из панели данных (справа) вытащим на соответствующие бэнды поля таблиц (главной и подчинённой). Получится примерно так:
Обратите внимание – бэнд "Данные 1 уровня" должен располагаться выше! Если разместить его под бэндом "Данные 2 уровня", FastReport сообщит об ошибке при запуске отчета.
Сейчас при запуске мы увидим, что список заказов одинаковый для каждого клиента и содержит все записи из таблицы Orders. Это произошло потому, что мы не включили фильтрацию записей в таблице Orders.
Вернемся к нашим источникам данных. У компонента Table2 установим свойство MasterSource = DataSource1. Таким образом мы установили связь "главный-подчиненный". Теперь надо задать условие фильтрации записей в подчиненном источнике. Для этого вызовите редактор свойства MasterFields у компонента Table2:
Нам надо связать два поля CustNo в обоих источниках. Для этого выберите индекс CustNo в списке сверху, выберите поля и нажмите кнопку "Add". Связка полей переместится в нижнее окно. После этого закройте редактор кнопкой ОК.
При запуске отчета FastReport сделает следующее. Выбрав очередную запись из главной таблицы (Customer), он установит фильтр на подчиненную таблицу (Orders). В таблице останутся только те записи, которые удовлетворяют условию Orders.CustNo = Customer.CustNo. Т.е. для каждого клиента будут показаны только его заказы:
Аналогичным образом можно строить отчеты, содержащие до 6 уровней данных.