https://imenno.ru/2018/03/22/434378/

Fast Report. Наследование на практике

© 2008 Ирина Цыбульникова

В статье Наследование в Fast Report я показала работу механизма наследования при создании отчетов в дизайнере Fast Report. Но создание отчета предполагает также написание сопутствующего кода в среде Delphi. В этой статье я покажу, как наследование в Delphi может облегчить жизнь разработчикам отчетов.

Начну со списка действий, которые должны быть выполнены в программе, чтобы пользователь увидел на экране наш FastReport отчет.

  • Загрузить отчет *.fr3 из каталога на диске в компонент TfrxReport;
  • Запросить у пользователя параметры для формирования отчета;
  • Подготовить и открыть все запросы, данные из которых будут отображаться в отчете;
  • Определить значения переменных отчета;
  • Подключить пользовательские функции;
  • Подключить экспортные фильтры.

Не все из перечисленных действий являются обязательными, поэтому при создании вашего отчета просто пропустите ненужные. Теперь остановимся подробнее на описании всех вышеперечисленных действий.

Перед получением отчета пользователю надо указать параметры на форме заказа отчета. Такая форма может быть вызвана из различных мест программы, в том числе из главного меню. Из этой формы пользователь может либо открыть предварительный просмотр отчета, либо экспортировать без показа в один из поддерживаемых Fast Report'ом форматов, а затем вернуться в эту же форму. Такой механизм работы с отчетами позволяет оперативно изменять отдельные параметры, не трогая остальные.


Общая схема получения отчета пользователем

Внешний вид каждой формы заказа в первую очередь зависит от самого отчета. На форме надо расположить компоненты для запроса у пользователя параметров ограничения, выбора метода группировки и сортировки данных различные для разных отчетов. Но вместе с тем у этих форм имеется достаточное количество одинаковых свойств и методов (для предварительного просмотра, экспорта, загрузки шаблонов и т.д.). Создадим одного предка для всех форм заказа, с таким расчетом, чтобы этот предок взял на себя однотипные действия по формированию любого отчета. Форма будет иметь такой вид:

Каждый класс-потомок этого класса-предка будет выполнять подготовку одного отчета. На свободном месте формы класса-потомка будут расположены компоненты для заказа параметров. Остановимся на основных элементах предка:

Во-первых, на предке нужен компонент traMainAsk: TIBTransaction, так как каждый отчет формируется в собственной транзакции. Очевидно, для отчета достаточно уровня изоляции транзакции "только для чтения". Соединение с базой установлено в главной форме проекта и указатель на него будет передаваться в форму заказа через public свойство MainDatabase типа TIBDatabase.

Во-вторых, нужен компонент frxMainReport: TfrxReport. Так как все формы отчета предполагается хранить в каталоге на диске, то устанавливаем свойство StoreInDFM:= False. Создадим обработчик OnLoadTemplate и в нем укажем путь на каталог, где хранится базовый отчет.

В третьих нужны компоненты для получения данных (idsFRReport: типа TIBDataSet) и передачи в отчет (frxMainDBDataset типа TfrxDBDataset). Свяжем эти компоненты друг с другом, а компонент idsFRReport с транзакцией traMainAsk и со свойством MainDatabase.

Создаем строковые поля класса Ftitle> - для наименования отчета, FRestriction - для описания выбранных ограничений и FRepTemplateName - для наименования шаблона.

И, наконец, основные методы класса предка:

  • UniFRepPrepare - virtual метод для подготовки отчета (загрузка шаблона отчета, открытие основного запроса, передачи параметров в отчет);
  • bbPrintClick - предварительный просмотр и печать отчета;
  • bbExportClick - экспорт в Microsoft Excel.

Создание форм заказа на основе этого класса-предка рассмотрим на примере ранее созданных отчетов "Платежи по видам оплат за период" и "Сумма оплат по дням за период" (см. статью Наследование в Fast Report). Создадим два класса потомка, наследованных на классе-предке и разместим на них компоненты для задания ограничений.

Вид формы заказа отчета "Платежи по видам оплат за период":

Вид формы форма заказа отчета "Сумма оплат по дням за период":

В каждом потомке создадим два обработчика: метод OnCreate - для определения полей класса Ftitle> и FRepTemplateName и переопределим метод UniFRepPrepare объявленный в классе-предке - для формирования запроса с учетом выбранных ограничений. Пример этих обработчиков смотрите в архиве example.zip.

Уже сделано вполне достаточно, чтобы получить на экране отчет. Все остальные действия по формированию выполнит наш класс-предок.

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

Так как отчеты создаются для программного комплекса бухучета, то часто возникает необходимость печатать на отчете сумму прописью. Реализовать такую возможность удобно с помощью подключаемых функций Fast Report. Например, можно сделать так: в базовом отчете разместим в подвале группы метку "Сумма прописью":

Метке "Сумма прописью" в обработчике OnBeforePrint напишем код, который будет вызывать пользовательскую функцию из Delphi:

Memo2.Text := 'Сумма прописью: ' + MoneyToStringFR(Trim(Memo2.Text));
В классе предке создадим метод MoneyToStringFR
function TfmAnUniAsk.MoneyToStringFR(ASumma: String): String;
var
VSumma: Double;
begin
if TryStrToFloat(ASumma,VSumma) then
Result:= MoneyToString(VSumma)
else
Result:= '-';
end;
Зарегистрируем нашу функцию.
frxMainReport.AddFunction('function MoneyToStringFR(ASumma: String): String');
У компонента frxMainReport создадим обработчик метода UserFunction:
function TfmAnUniAsk.frxMainReportUserFunction(const AMethodName: string; var Params: Variant): Variant;
begin
if AMethodName = 'MONEYTOSTRINGFR' then
Result:= MoneyToStringFR(Params[0]);
end;
Теперь эту функцию можно использовать в скрипте отчета. В обработчике OnBeforePrint напишем код к уже существующему коду в базовом отчете:
Memo2.Text := SUM(<frxDetailList."IVD_FULL_COST">);
Результат работы подключенной функции выглядит так:

Пользователю предоставляется возможность получить отчет несколькими способами: через окно предварительного просмотра, непосредственно на принтер, выгрузкой в Microsoft Excel. В последних двух случаях используется вызов метод TfrxReport.PrepareReport(True), где параметр True удаляет ранее подготовленный отчет, если такой имеется. На форме предка есть кнопка для экспорта в Excel. Ее обработчик должен содержать такие команды:

if UniFRepPrepare then
begin
frxMainReport.PrepareReport(true);
frxMainReport.Export(dmFastRepExport.frxXLSExport);
end;

Очевидно, что таким способом можно подключить любой из имеющихся в FastReport экспортных фильтров. Или сразу все фильтры - это зависит от технического задания и фантазии разработчика. Для печати непосредственно на принтер обработчик события будет выглядеть также, только вместо функции Export используйте функцию Print.

В документации ProgrammerManual, гл.1.7 рекомендуется устанавливать экспортные фильтры на форму для подготовки отчета. В нашем случае эти фильтры следовало бы поставить на класс-предок форм заказа отчета. Но в этом случае при создании каждой формы заказа создавался бы и свой набор экспортных фильтров. Если после закрытия отчета класс уничтожить, то проблемы нет, но если необходимо сохранить этот класс в памяти, то при создании следующей формы заказа количество экспортных фильтров в памяти увеличится в два раза. Тогда меню для экспорта отчета имеет следующий вид:

Я предлагаю такое решение. Поместить все экспортные фильтры на отдельный модуль данных, который создается и уничтожается один раз вместе с приложением. Таким образом, свойства экспортных фильтров настраиваются один раз, и фильтры находятся только один раз в адресном пространстве программы. При экспорте отчета необходимо уникальное имя файла для того, чтобы его можно было легко найти. Следующая процедура позволяет присвоить всем экспортным фильтрам уникальное имя файла.

procedure TdmFastRepExport.WriteFRPath(ANameFile: String);
var
i: Integer;
VfrxExportClass: TfrxCustomExportFilter;
begin
if ComponentCount > 0 then
begin
for i:= 0 to ComponentCount - 1 do
begin
if Components[i] is TfrxCustomExportFilter then
begin
VfrxExportClass:= Components[i] as TfrxCustomExportFilter;
VfrxExportClass.FileName:= Trim(ANameFile);
end;
end;
end;
end;

В классе-предке остается только вызвать функцию WriteFRPath, которая выполнит настройку каждого фильтра:

dmFastRepExport.WriteFRPath(NewFileName(AReporttitle>));

В качестве итога хочу сказать, что для выполнения моей задачи написание класса-предка в среде Delphi позволило:

  • значительно упростить создание каждого отчета за счет исключения одинаковых операций. Посмотрите примеры в example.zip. Код наследованного модуля минимален. Фактически в классе потомке только собирается SQL-запрос и присваиваются выбранные пользователем параметры;
  • пользовательский интерфейс при заказе каждого отчета теперь реально однотипный, и это в проекте, имеющем более сотни отчетных форм! И изменить этот интерфейс легко, для этого надо корректировать только класс предок;
  • каждый отчет в проекте может использовать, если необходимо, подключаемые функции, описанные в классе-предке;
  • при создании каждого нового отчета можно не заботиться об экспорте. У всех отчетов в проекте экспортные возможности одинаковые. Не приходится следить за созданием объектов, опасаясь дублирования фильтров;
  • класс-предок можно заставить выполнять и другие задачи, например, положить на него компоненты для построения кросс-таблиц, вставки штрих-кодов, для написания диалоговых форм в отчете.

К статье приложен архив example.zip с примерами описанных в статье классов, модуля с экспортными фильтрами, а также шаблоны отчетов.

Copyright© 2008 Ирина Цыбульникова Специально для Delphi Plus



Новости за месяц

  • Январь
    2022
  • Пн
  • Вт
  • Ср
  • Чт
  • Пт
  • Сб
  • Вс