Печать итоговой суммы по группе в заголовке группы.
Этот
довольно часто используемый прием требует использования скрипта. Ведь в
обычном отчете значение суммы становится доступным только после того,
как будут обработаны все записи группы. Чтобы вывести сумму в заголовке
группы (т.е. до того, как будет обработана группа), используется
следующий алгоритм:
- отчет делается двухпроходным;
- на первом проходе считается сумма по каждой группе и сохраняется в каком-нибудь массиве;
- на втором проходе значения извлекаются из массива и печатаются в заголовке группы.
Продемонстрируем,
как решить эту задачу двумя способами. Для начала создадим новый проект
в Delphi, на форму положим компоненты TQuery, TfrxReport,
TfrxDBDataSet. Настроим их следующим образом:
Query1:
DatabaseName = 'DBDEMOS' SQL =
select * from customer, orders where orders.CustNo = customer.CustNo
order by customer.CustNo, orders.OrderNo frxDBDataSet1:
DataSet = Query1
UserName = 'Group'
Зайдем
в дизайнер и подключим наш источник данных к отчету. В настройках
отчета (пункт меню "Отчет | Настройки") включим двойной проход. Добавим в
отчет два бэнда: "Заголовок группы" и "Данные 1 уровня". В редакторе
бэнда "Заголовок группы" укажем условие – поле данных Group.CustNo.
Дата-бэнд привяжем к источнику данных Group и разместим объекты
следующим образом:
Выделенный на рисунке объект (его имя – Memo8) мы используем для вывода суммы.
Способ 1.
Мы
используем в качестве массива для хранения сумм класс TStringList.
Значения будем хранить в виде строк. При этом первая строка в списке
будет соответствовать значению первой группы, и т.д. Для подсчета номера
группы будет использована целочисленная переменная, которую мы будем
увеличивать после печати очередной группы.
Итак, наш скрипт будет выглядеть следующим образом:
var List: TStringList; i: Integer; procedure frReport1OnStartReport(Sender: TfrxComponent);
begin List := TStringList.Create; end;
procedure frReport1OnStopReport(Sender: TfrxComponent);
begin
List.Free; end;
procedure Page1OnBeforePrint(Sender: TfrxComponent); begin
i := 0; end;
procedure GroupHeader1OnBeforePrint(Sender: TfrxComponent);
begin if Engine.FinalPass then
Memo8.Text := 'Sum: ' + List[i]; end;
procedure GroupFooter1OnBeforePrint(Sender: TfrxComponent);
begin
List.Add(FloatToStr(<SUM(<Group."ItemsTotal">,MasterData1)>)); Inc(i);
end;
begin end.
По именам процедур можно видеть, какие события мы использовали:
Report.OnStartReport,
Report.OnStopReport, Page1.OnBeforePrint, GroupHeader1.OnBeforePrint,
GroupFooter1.OnBeforePrint. Что касается первых двух событий, то они,
как уже говорилось, вызываются в начале и в конце отчета,
соответственно. Чтобы создать обработчики для этих событий, надо
выделить объект "Отчет" в окне "Дерево отчета" – его свойства появятся в
инспекторе объектов. Далее действуем стандартным образом –
переключаемся на закладку "События" инспектора и создаем обработчики.
Почему
мы не воспользовались для создания списка List главной процедурой, а
сделали это в событии OnStartReport? Потому, что созданный объект надо
после завершения отчета освободить. Поэтому логично создавать объекты в
событии OnStartReport, а освобождать их в OnStopReport. В других случаях
(когда не нужно освобождать память) можно пользоваться главной
процедурой для инициализации переменных.
С
созданием и освобождением объекта List все понятно. Теперь рассмотрим,
как работает скрипт. В начале страницы счетчик текущей группы
(переменная i) сбрасывается в 0 и увеличивается на единицу после печати
каждой группы (в событии GroupFooter1.OnBeforePrint). В этом же событии в
список добавляется вычисленное значение суммы. Событие
GroupHeader1.OnBeforePrint на первом проходе не срабатывает (проверка
Engine.FinalPass). На втором проходе (когда список List заполнен
значениями), в этом событии извлекается значение, соответствующее
текущей группе, и записывается в текст объекта Memo8, который и
показывает сумму в заголовке группы. В готовом отчете это выглядит так:
Как видим, алгоритм достаточно простой. Но и его можно упростить.
Способ 2.
Мы
используем в качестве массива для хранения сумм список переменных
отчета. Как мы помним, обращение к таким переменным осуществляется с
помощью функций Get и Set. Это избавит нас от необходимости создавать
лишние объекты и освобождать память. Наш скрипт будет следующим:
procedure GroupHeader1OnBeforePrint(Sender: TfrxComponent); begin if Engine.FinalPass then memo8.Text := 'Sum: ' + Get(<Group."CustNo">);
end;
procedure GroupFooter1OnBeforePrint(Sender: TfrxComponent); begin
Set(<Group."CustNo">,FloatToStr(<SUM(<Group."ItemsTotal">,MasterData1)>));
end; Begin
end.
Как
видно, скрипт значительно упростился. Код в обработчике
GroupFooter1.OnBeforePrint устанавливает значение переменной с именем,
равным номеру клиента (можно использовать любой идентификатор,
однозначно идентифицирующий клиента, например его имя
<Group."Company">). Если такой переменной нет – она создается,
если есть – меняется ее значение. В обработчике
GroupHeader1.OnBeforePrint извлекается значение переменной с номером
текущей группы.
|