Fast Report. Про итоги по страницам

Приемы и подходы, обмен опытом

Модераторы: Screw, larin

Ответить
Hershy
рег.отделение
Сообщения: 64
Зарегистрирован: Вт, 03/03/2009 07:53
Имя Фамилия: Николай Воронцов
Откуда: Галактика-Урал
Контактная информация:

Fast Report. Про итоги по страницам

Сообщение Hershy »

Добрый день.

Занялись фастрепортом. Все более менее понятно. Данные выводятся какие нужно, да только вот не так как нужно.

Задача: Есть отчет, на каждой странице отчета должны быть итоги по странице (именно по странице, а не накапливающиеся итоги по всем предыдущим страницам), в конце отчета необходимо вывести плюс ко всему еще и итоги по всей таблице.

Проблема: Создали отчет, в шаблоне фастрепорта указали как полагается MasterData и PageFooter + Footer. Отчет формируется, данные есть. Только:
1. итоги по странице оторваны от основной таблицы;
2. итоги по странице включают все предыдущие суммы;
3. На последней странице итоги по странице находятся после итогов по всей таблице (это мне вообще не понятно зачем сделали так)

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

В одной теме (по ТОРГ-12) прочитал, что можно закодировать все это дело уже в шаблоне фастрепорта. Однако, хотелось бы узнать, есть ли другой, более человечный способ решить такую простенькую на первый взгляд задачу, а не черезж......е методы с использованием скриптов. Если такой возможности нет, придется делфю вспоминать :(

Спасибо заране!
Аватара пользователя
larin
топ-софт
Сообщения: 228
Зарегистрирован: Пн, 10/09/2007 12:13
Имя Фамилия: Михаил Ларин
Откуда: ТопCофт
Контактная информация:

Re: Fast Report. Про итоги по страницам

Сообщение larin »

Hershy писал(а): Проблема: Создали отчет, в шаблоне фастрепорта указали как полагается MasterData и PageFooter + Footer. Отчет формируется, данные есть. Только:
1. итоги по странице оторваны от основной таблицы;
2. итоги по странице включают все предыдущие суммы;
3. На последней странице итоги по странице находятся после итогов по всей таблице (это мне вообще не понятно зачем сделали так)
Эти три недочета решить можно.
Hershy писал(а): Посмотрел на форумах и фастрепорта и на этом форуме, ответы расплывчатые, конкретного решения не предложено, кроме как "Смотрите мануал, думайте мозгами, читайте на нашем форуме". Про думать мозгами согласен :), но как не ломал голову не получается сделать что нужно.
Когда впервые столкнулся с фастрепортом, поверьте было тоже самое "ломал голову, не получалось сделать что нужно". Чтение документации мне действительно тогда помогло. Не поленитесь почитать. Документация по FR действительно очень качественная. Многие аспекты программирования форм не так очевидны как хотелось бы. Документация мысли упорядочивает.

Пришлите исходный код вашей формы (FR3 + VIP) мне на e-mail. На основе вашего примера попытаюсь сделать конкретный пример. Позже опубликую его.
Hershy писал(а): В мануале вообще нарисована ситуация, когда итоги по таблице идут перед итогами по странице - интересно есть где-нибудь такие отчеты вообще (риторический вопрос).
Были но сейчас не вспомню.
Hershy писал(а): Однако, хотелось бы узнать, есть ли другой, более человечный способ решить такую простенькую на первый взгляд задачу, а не черезж......е методы с использованием скриптов. Если такой возможности нет, придется делфю вспоминать :(
Спасибо заране!
Решение скорее всего будет из области применения скриптов. Вообще в случае FastReport баланс между использованием скриптов и не-скриптов (блоков FR-Engine-а) поможет находить только опыт.
ilshat
заказчик партнера
Сообщения: 63
Зарегистрирован: Чт, 05/06/2008 11:09
Имя Фамилия: Ильшат Фахрисламов
Откуда: Каустик

Re: Fast Report. Про итоги по страницам

Сообщение ilshat »

Hershy писал(а):на каждой странице отчета должны быть итоги по странице (именно по странице, а не накапливающиеся итоги по всем предыдущим страницам), в конце отчета необходимо вывести плюс ко всему еще и итоги по всей таблице.
Все таки мануал вы так и не читали. Хотя вас четко послали именно в этом направлении!!!
Вот прямо копи-паст из мануала:
  • Итоги по странице и по отчету
    Довольно часто приходится отображать в отчете итоговое значение по
    странице или по всему отчету. Это также делается с помощью агрегатных функций.
    Рассмотрим это на нашем примере.
    Как видим, мы добавили бэнд "Подвал отчета" и объект "Текст" с суммой на
    бэнды "Подвал отчета" и "Подвал страницы". Это все, что нам нужно.
Hershy писал(а): 3. На последней странице итоги по странице находятся после итогов по всей таблице (это мне вообще не понятно зачем сделали так)
А мне непонятно что вы хотите получить. Вам нужен итог по странице или итог под данными?
ФастРепорт выдал все вполне логично итоги по всей табле полюбому всегда будут выше футера страницы. Или что у нас уже можно футер страницы сувать куда хотим?
Колонтитулы в Ворде вроде как тоже расположены внизу и вверху страницы и никак не умеют летать куда хотят.
Hershy
рег.отделение
Сообщения: 64
Зарегистрирован: Вт, 03/03/2009 07:53
Имя Фамилия: Николай Воронцов
Откуда: Галактика-Урал
Контактная информация:

Сообщение Hershy »

Михаил, спасибо за четкие ответы.
Для остальных хотелось бы поведать историю, как на двери к одному профессору вывеска была, гласящая "Перед тем как постучать, подумай, есть ли тебе что сказать".

А теперь по теме: после не долгих мучений у нас вышло следующее:

1. Интерфейс для потока:

Код: Выделить всё

TABLE STRUCT LOCAL INVVZ "ТАБЛИЦА В ПАМЯТИ"(
  NREC   : COMP
, NAME   : STRING
, SUMM   : DOUBLE
)
WITH INDEX(
  INVVZ01 = NREC(UNIQUE, SURROGATE)
);
Interface i087INV17 '' Cyan;
var stmt,mt, i : longInt;
CREATE VIEW AS SELECT * FROM INVVZ;
    dataStream PrjMngFR01Stream    (
        [ NAMEORG ] sGetTune('MYORG');
        table INVVZ    (
            [ NAME ] INVVZ.NAME;
            [ SUMM ] INVVZ.SUMM;
        );
    )
    end;
handleevent
cmInit:{
    stmt  := sqlAllocStmt;
    sqlExecStmt(stmt, 'SELECT TOP 40 NAME FROM KATORG');
    sqlFetchIntoMT(stmt, mt);
    var rec : array[1..1] of variant;
    if(sqlNavigateMT(mt, ffGetFirst, rec) = tsOk) {
        i := 1;
        insert INVVZ
        SET
        INVVZ.NAME := rec[1],
        INVVZ.SUMM := i++
        ;
        while (sqlNavigateMT( mt, ffGetNext, rec ) = tsOk){
            insert INVVZ
            SET
            INVVZ.NAME := rec[1],
            INVVZ.SUMM := i++
            ;
        }
    }
    sqlFreeStmt(stmt);
    sqlFreeMT(mt);
    RunFReport(PrjMngFR01Stream, 'PrjMngRep', true);
    CloseInterface(cmOK);
}
end;
end.
2. Файл фастрепорта
2.1. Добавляем MasterData1, с двумя полями NAME и SUMM из потока INVVZ
2.2. Добавляем ColumnFooter1, с двумя полями:
2.2.1. Первое поле для итогов по странице со следующим кодом [SUM(<INVVZ."SUMM">,MasterData1,1)]
2.2.2. Второе поле Memo6 для итогов по таблице со следующим кодом [SUM(<INVVZ."SUMM">,MasterData1,2)]. Для этого поля необходимо указать свойство Visible = FALSE
2.3. Добавляем Footer1, указать свойство Visible = FALSE.
2.4. Создать обработчик OnBeforePrint для Footer1:

Код: Выделить всё

procedure Footer1OnBeforePrint(Sender: TfrxComponent);
begin
  Memo6.Visible := TRUE;
end;
2.5. Необязательное: Указать в настройках листа, нижнюю границу = 0. Это необходимо, по-моему, т.к. для невидимой Memo6 на страницах, где она не выводится, все-таки выделяется место, и получается лишняя строчка пустая в конце каждой страницы.

У нас получилось, что хотелось, возникли, конечно, и другие вопросы, которые, надеюсь, мы обсудим на этом форуме, касающиеся некоторых деталей FastReporta.

Во вложении файл интерфейса и файл Fastreporta

По поводу предоставленного варианта решения задачи, принимаются абсолютно все замечания, и будет очень хорошо, если появятся какие-нибудь дополнения.

Предлагаю обсудить следующие вопросы:
1. В мануале для DSQL используется понятие таблицы в памяти для метода sqlFetchIntoMT. Как не пытались, не получается использовать ее для потока в FastReport. Получается, что таблица в памяти, созданная TABLE STRUCT и таблица в памяти, созданная методом sqlFetchIntoMT - это абсолютно разные сущности (интересно почему понятия используют одно, а означает оно разные вещи, если я, конечно, все правильно понимаю). Собственно вопрос в том, можно как-нибудь использовать таблицу в памяти DSQL для Fastreport напрямую, а не так, как описано в примере выше?
2. Для форм строгой отчетности есть такие требования, что если подписи в конце отчета не входят полностью на страницу, то нужно их переносить на следующую страницу, при этом необходимо также, перенести хотя бы одну строчку с данными на эту же, последнюю, страницу с итогами по странице (т.е. по одной записи) и с итогами по таблице (при этом на предыдущей странице, как становится понятно, итоги по таблице выводить не нужно). Вот как это сделать, для нас вопрос большой, может подскажет кто?
Вложения
testFast.rar
Исходники для теста
(10.95 КБ) 481 скачивание
Аватара пользователя
larin
топ-софт
Сообщения: 228
Зарегистрирован: Пн, 10/09/2007 12:13
Имя Фамилия: Михаил Ларин
Откуда: ТопCофт
Контактная информация:

Сообщение larin »

Спасибо за пример. Подготовка стати и примера займет некоторое время. Позже опубликуем.
Аватара пользователя
larin
топ-софт
Сообщения: 228
Зарегистрирован: Пн, 10/09/2007 12:13
Имя Фамилия: Михаил Ларин
Откуда: ТопCофт
Контактная информация:

Сообщение larin »

В части ответа на вопрос о способах подготовки данных для печати в 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.
Надеюсь этот способ вам подойдет.

Следующем сообщении будет описан прием программирования отчета с итогами по странице средствами FastReport. От способа получения данных разработка формы уже не зависит.
Аватара пользователя
larin
топ-софт
Сообщения: 228
Зарегистрирован: Пн, 10/09/2007 12:13
Имя Фамилия: Михаил Ларин
Откуда: ТопCофт
Контактная информация:

Сообщение larin »

Полный текст статьи, примеры исходного и результатов находятся здесь:
http://laalaa.googlecode.com/files/Page ... Report.rar

Как устроена Галактика? - Отчет с итогами по странице.

Иногда возникают задачи, где нужно разработать отчет. Что бы на каждой странице этого отчета выводились итоги по странице. Чтобы в конце отчета выводились ито-ги по всему отчету в целом. Такие отчеты можно разрабатывать с помощью генера-тора отчетов 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;
 
Аватара пользователя
larin
топ-софт
Сообщения: 228
Зарегистрирован: Пн, 10/09/2007 12:13
Имя Фамилия: Михаил Ларин
Откуда: ТопCофт
Контактная информация:

Сообщение larin »

Статья так же опубликована на сайте Reportingfor - Отчеты для всех"
http://www.reportingfor.info/ru/news.php?extend.81
Ответить