Полный текст статьи Как устроена Галактика? - Отчет с итогами по странице с примерами исходного и результатов находятся здесь:
http://laalaa.googlecode.com/files/Page ... Report.rar
Методы разработки отчетов FastReport с итогами по странице.
А также способы выдачи на печать данных, полученных через DirectSQL.
Обсуждаются на минском форуме:
http://forum.galaktika.by/viewtopic.php?t=538
Как устроена Галактика? - Отчет с итогами по странице.
Модераторы: m0p3e, edward_K, Модераторы
Как устроена Галактика? - Отчет с итогами по странице.
Иногда возникают задачи, где нужно разработать отчет. Что бы на каждой странице этого отчета выводились итоги по странице. Чтобы в конце отчета выводились ито-ги по всему отчету в целом. Такие отчеты можно разрабатывать с помощью генера-тора отчетов FastReport.
Требования к отчету в каждом конкретном случае могут отличаться. Для простых случаев вполне подойдет простой метод который описан в документации по FastReport. Метод состоит в том чтобы поместить агрегатную функцию SUM(expression, band, flags) на бэнды PageFooter -"Подвал страницы" и ReportSummary - "Подвал отчета". Косметические поправки к оформлению, такие как "не отрывать PageFooter от данных", можно корректировать с помощью не-сложных скрипов.
Например:Этот метод хорошо подходит когда нужно вывести итоги просто цифрой, когда цифра с итогами уместится в одну строку фиксированной высоты, кода нет специальных требований к оформлению бланков строгой отчетности.Код: Выделить всё
var Y: extended; procedure MasterData1OnAfterPrint(Sender: TfrxComponent); begin Y:=Engine.CurY; end; procedure PageFooter1OnBeforePrint(Sender: TfrxComponent); begin Engine.CurY:=Y; end;
Однако в реальности отчеты могут быть более сложные. К оформлению таких отче-тов могут предъявляется различные дополнительные требования. Однако и в этих случаях FastReport позволяет справиться с этой задачей.
Для примера рассмотрим отчет, к которому предъявляются следующие требования:
1) В отчет должна выводиться таблица с наименованиями товаров и суммами. Наименование товара может не уместится в одну строку текста, при этом высота строки таблицы должна автоматически увеличиваться, чтобы поместилось на-именование.
2) Для каждой страницы отчета нужно вывести итоги по странице цифрой и прописью.
3) Сумма итого по странице прописью также может не уместить в одну троку текста, при этом на листе под вывод итоговой суммы должно резервироваться ровно столько места, сколько для этого требуется.
4) Итоги по странице должны выводиться непосредственно после данных.
5) Отчет должен помещается на листе формата А4. Должны быть зарезервиро-ваны поля 1см сверху, снизу, слева и справа.
6) Заголовок таблицы должен повторяться на каждой странице.
7) На первой странице должен выводиться заголовок отчета с датой и названием организации.
8 ) На последней странице отчета, непосредственно после итогов по странице должны выводиться итоги по всему отчету цифрой и прописью.
9) Сумма итого по отчету прописью также может быть многострочной, при выводе на печать должно резервироваться достаточно места
10) Непосредственно после итогов по отчету, должны выводиться подписи ди-ректора главного бухгалтера и место для печати.
11) Если в конце последней страницы недостаточно места для вывода итогов по листу, итогов по отчету и подписей. То необходимо сформировать новую стра-ницу и перенести на нее заголовок таблицы и одну послюню строку с данными по товару.
Для реализации такого отчета можно предложить следующий метод или алгоритм:
1) На первом этапе определить общее количество строк таблицы и сосчитать итоговую сумму по всей ведомости.
2) Вторым шагом, определить, какую высоту займет текст с суммой итого по ве-домости. Текст для суммы можно получить с помощью функции FloatToWords. Высоту, которая необходима, для выдачи этого текса можно получить с помо-щью метода TfrxMemoView.CalcHeight (метод определен в базовом классе TfrxStretcheable)
3) Следующим этапом последовательно анализировать строки перед выводом в отчет. Определить высоту строки. Увеличить счетчик с итогом по странице и оп-ределить высоту для текста с итогом. С помощью тех же функций FloatToWords и TfrxMemoView.CalcHeight
4) Проверить умещается ли по высоте текущая строка вместе текущим стогом по странице на свободном месте страницы (метод Engine.FreeSpace). Если уме-щается, то вывести на печать текущую строку и перейти к обработке следующей строки. Если же текущая строка вместе с ее расчетным итогом уже не умещается на странице, то уменьшить сумму итога по станице на сумму текущей строки, вывести итог по предыдущей строке, дать генератору команду сформировать новую страницу, вывести на печать шапку таблицы и текущую строку.
5) Для последней строки ведомости в расчете занимаемого пространства (см пункт 3) учитывать также высоту итогов по всему отчету и высоту необходимую для вывода подписей.
6) Последний этап алгоритма, для последней страницы вывести на печать итоги и подписи.
Реализацию данного алгоритма проще всего поместить в обработчик события TfrxReportPage.OnManualBuild.
На мой взгляд, предложенный подход достаточно прост и понятен. Хотя это не единственно возможный вариант реализации. В примере задействована только не-большая часть возможностей FastReport.
Примечание: Основная трудность, которая возникла у меня при подготовке при-мера, в исследовании того как на самом деле работает функция TfrxMemoView.CalcHeight. К сожалению, эта функция не описана в документации. Как выяснилось в процессе тестирования отчета. Эта функция дает не правильный ре-зультат для полей отчета, значение которых определяется полем БД или выражением в тексте. Как выяснилось функция TfrxMemoView.CalcHeight корректно вычисляет высоту только для со статически заданным текстом. По этому если вы обратите вни-мание на исходный код примера - там везде применяется следующий прием:
- У расчетных объектов TfrxMemoView свойстово AllowExpressions="False"
- Расчет текста и его высоты полностью задан в скрипте
Код: Выделить всё
MemoName.text := <SUBTOTALDATASET."NAME">; RatedFooterHeight := MemoName.CalcHeight;
В части ответа на вопрос о способах подготовки данных для печати в FastReport на стороне языка VIP и выполнения запросов средствами DSQL. Порекомендую следующее:
На мой взгляд совершенно излишним будет стремление реализовать подход промежуточной выгрузкой запроса DSQL в таблицу в памяти, а затем формировать поток данных для печати на таблице в памяти. Более простым и эффективным будет подход если запрос DSQL сразу связать с потоком данных для FastReport. Рекомендую не использовать промежуточные таблицы в памяти вовсе.
Конструкция datastream может содержать не только элементы связанные с таблицами, но и элементы связанные с программируемыми источниками данных. Программируемые источники данных задаются с помощью ключевого слова dataset. С помощью dataset можно запрограммировать получение данных из произвольного источника любого происхождения (из файлов, из массивов в памяти, из запросов DSQL, из фалов DBF и т.д. )
Пример исходного кода:Надеюсь этот способ вам подойдет.Код: Выделить всё
Interface PageTotalDemoReport; create view; var stmt: longint; // запрос var sName, sBarkod: string; // результаты запроса var RandomSumma: double; // случайная сумма var LastRandomCounter: longint; // счетчик псевдослучайной последовательности // функция генерации следующего числа псевдослучайной последовательности // результат случайное число от 0 до Range function NextRandomNumber(Range: longint): double; { LastRandomCounter := (1664525 * LastRandomCounter + 1013904223) mod 2147483647; result := abs(double(LastRandomCounter mod Range)); } datastream SubTotalStream ( // реквизиты организации для заголовка и подножия отчета [NameOrg] sGetTune('MyOrg'); [Boss] sGetTune('Boss'); [MainBuh] sGetTune('MainBuh'); [MainCashier] sGetTune('MainCashier'); // элемент потока данных управляемый программно dataset SubTotalDataSet ( [Name] sName; [Barkod] sBarkod; [Summa] RandomSumma; ); ) handleEvent dataset SubTotalDataSet cmPreProcess: { // Инициализация запроса Direct-SQL stmt := sqlAllocStmt; sqlBindCol(stmt, 1, sName); sqlBindCol(stmt, 2, sBarkod); sqlExecStmt(stmt, 'SELECT TOP 100 NAME, BARKOD FROM KatMc'); // Инициализация псевдослучайной последовательности // Чтобы каждый раз, когда FastReport будет начинать // выполнять этот запрос суммы формировались в той же последовательности LastRandomCounter := 100; } cmOnProcess: { // Получить очередную порцию данных по запросу Direct-SQL // Событие cmOnProcess приходит каждый перед получением // первой и последующих записей if(sqlFetch(stmt) = tsOk) { // Функция ContinueDataset сигнализирует регенератору // отчета, что строка данных существует ContinueDataset; // Сформировать очередную случайную сумму с копейками в передах миллиона :) RandomSumma := NextRandomNumber(100000000) / 100; } } cmPostProcess: { sqlFreeStmt(stmt); } end; end; handleEvent cmInit: { // Рекомендую запускать на выполнение отчет с таким параметрами RunFReport(SubTotalStream, '', false). // Пустое имя означает - если отчетов несколько, то будет предложено окно для выбора отчета. // False - означает что отчет будет запущен в режиме выполнения. // Если нужно войти в дизайнер, то в окне выбора отчета нажимайте F4. // Параметр файла конфигурации [Forms] Fcenv=On принудительно включает // показ окна выбора отчетов даже если отчет в списке только один. RunFReport(SubTotalStream, '', false); Abort; } end; end.
Статья так же опубликована на сайте Reportingfor - Отчеты для всех"
http://www.reportingfor.info/ru/news.php?extend.81
http://www.reportingfor.info/ru/news.php?extend.81
-
- Постоянный обитатель
- Сообщения: 188
- Зарегистрирован: 17 июн 2008, 17:07
- Откуда: Москва
- Контактная информация:
Re: Как устроена Галактика? - Отчет с итогами по странице.
Очень полезная вещь! наконец и я до нее добрался ![Smile :smile:](./images/smilies/239.gif)
![Smile :smile:](./images/smilies/239.gif)