Создание Генератора отчета независимого от *.exe и файлов *.fr3
Перечитал массу веток, но так и не смог найти того чего хочу. Проблему кратко описал в теме.
Моя среда: Oracle, DelphiXE, Odac.
Отчеты только из хранимых процедур с In и Out параметрами.
In параметры - для критериев выборки это п.2
Out параметры - для дополнения инфы к отчету это п.3
Теоритическая задумка такова:
1. В репорте, в диалоге в событии клик кнопки переношу значения полей в заранее указанные UserVariables
2. В frxDBDataSet в событии BeforeOpen из UserVariables перекачиваю в In парметры OdacStoredProc и открываю курсор для MasterDataset
3. Возвращенные параметры OdacStoredProc заношу в репорт через событие OdacStoredProc.AfterOpen
Но эта теория.
Пожскажите пожалуйста проф. подход?
Т.е. как положено делать эти операции более красиво? Особенно не нравиться п.3, мне нужно его выполнить перед отображением отчета.
Прошу авторизацию по ADO не предлагать.
Моя среда: Oracle, DelphiXE, Odac.
Отчеты только из хранимых процедур с In и Out параметрами.
In параметры - для критериев выборки это п.2
Out параметры - для дополнения инфы к отчету это п.3
Теоритическая задумка такова:
1. В репорте, в диалоге в событии клик кнопки переношу значения полей в заранее указанные UserVariables
2. В frxDBDataSet в событии BeforeOpen из UserVariables перекачиваю в In парметры OdacStoredProc и открываю курсор для MasterDataset
3. Возвращенные параметры OdacStoredProc заношу в репорт через событие OdacStoredProc.AfterOpen
Но эта теория.
Пожскажите пожалуйста проф. подход?
Т.е. как положено делать эти операции более красиво? Особенно не нравиться п.3, мне нужно его выполнить перед отображением отчета.
Прошу авторизацию по ADO не предлагать.
Комментарии
Оно работаетв аккурат между Диалоговым окном и Запуском отчета.
Выше описаные операции п.2 и п.3 заложил в это событие, работает отлично.
procedure TfRep.frRepBeforePrint(Sender: TfrxReportComponent);
var
s: string;
i,k: integer;
begin
if pOpen then exit;
pOpen := true; //Этот флаг сделал, т.к. событие вызывается почему то несколько раз
//Название процедуры селекта(курсора) в таблице БД, как и сам файл fr3(но его я загрузил в другом методе)
s := spEdit.FieldByName('proc_select_' + copy(aSp.Name,length(aSp.Name),1)).AsString;
if s = '' then exit;
aSp.StoredProcName := s;
aSp.PrepareSQL;
//Здесь я выполняю п.2 перекачиваю полученные из диалога значения переменных в параметры вызова процедуры
for i := 0 to aSp.ParamCount - 1 do
with aSp.Params do
begin
if (ParamType <> ptInput) and (ParamType <> ptInputOutput) then continue;
k := frRep.Variables.IndexOf(name);
if k <> -1 then Value := frRep.Variables.Items[k].Value;
end;
aSp.Close;
aSp.Open;
//Здесь выполняю п.3 закачиваю полученные параметры из процедуры в отчет, типа кто запускал отчет и т.п.
for i := 0 to aSp.ParamCount - 1 do
with aSp.Params do
begin
if (ParamType <> ptOutput) and (ParamType <> ptInputOutput) then continue;
k := frRep.Variables.IndexOf(name);
if k <> -1 then
begin
if (DataType = ftString) or (DataType = ftMemo) or (DataType = ftWideString) or (DataType = ftWideMemo) or (DataType = ftFixedChar) or (DataType = ftFixedWideChar) then
frRep.Variables.Items[k].Value := '''' + VarAsType(Value,varString ) + ''''
else frRep.Variables.Items[k].Value := Value;
end;
end;
end;
Теперь осталось совсем ничего чтобы проект был не зависим от exe и fr3 файлов.
Судя о работоспособности отчета, я выяснил, что MasterData1 на лету цепляет frxDBDataset. Это радует.
Но теперь уперся в пока не разрешимую проблему. Кажется GroupHeader на лету не цепляет frxDBDataset
и у меня поэтому щас пока не разрешимая ошибка GroupHeader1: Ошибка в выражении 'dsProc_select_1."ROLENAME"': Identifier expected
В проекте где все зашито, т.е. нет динамической загрузки отчета и названия процедуры, все ок.
Я свободен от exe файла проекта и fr3 файлов!!!
Сделал так:
1. В БД Оракл создал таблицу с названием 5 процедур курсоров и 5 процедур lookup, а также файл fr3.
2. Создаем отчет fr3 с использованием строго опредленных имен компонентов OdacStoredProc1-5.
- создаем юзер переменные одинаковые с переменными хранимых процедур курсоров
- в диалог окне по событию Ok кнопки закачиваем параметры окна в юзер переменные
3. Мой класс делает следующее:
- создает 5 процедур курсоров и 5 процедур lookup и заполняются названия на основе таблицы п.1.
- загружает файл fr3
- в диалог окна заполняем Caption название отчета
- открываем все 5 lookup процедур(конечно только указанные в таблице), процедуры курсоров пока закрыты
- запускается отчет frxReport.ShowReport
- в событии frxReport.BeforePrint которое выполняется после диалога, но до запуска отчета
+ загружаются юзер переменные отчета(в которых уже п.2 залит) в схожие In и InOut параметры всех 5 процедур курсоров(которые заполнены в таблице)
+ открываются методом Open все 5 процедур курсоров
+ перекачиваются Out и InOut параметры в отчет
Все отчет получен!!! Классы форм не используются!
В самой программе я просто загружаю из БД список отчетов в опредленное меню "Отчеты", а в tag-ах у меня храниться ID
Невероятная гибкость и свобода/независимость от обновлений EXE и FR3 файлов.
Мне достаточно в дизайнере настроить отчет, создать диалог, даже dblookup могу юзать, и залить в БД. Все!!!
Никаких геморов с перекомпиляцией, копированием fr3 файлов на юзерский комп, вешалкой с окном критериев!
Если понравилось, то могу позже и исходниками подолиться.
Шаблоны отчётов можно хранить в базе, критерии выборки можно передавать из диалоговой формы отчёта в программу через пользовательскую функцию
Кроме того, можно использовать internal-датасеты ODAC для FR для создания полностью автономных отчётов (если поддержка хранимых процедур реализована devart)
Вы не могли бы описать кратко, как вы используете/добавляете отчеты в проекте, не компилируя exe и не копируя fr3 на клиентский комп юзеров?
http://www.fast-report.com/en/forum/index.php?showtopic=4853
Вы жжете ).
Стоит ли мне продолжать с вами обсуждать топик, если вы путаете методы с идеей ?
Причем тут блоб? Или как ложить в БД fr3? Заострение внимания на этих элементарных вещах не было моей целью...
Что с того что вы сохранили fr3 в БД ? А дальше то что?
В моей идее идет полная независимость не только от exe или fr3, а также почти САМОЕ важное независимость от многих
видов отчетов(разных если хотите). Простое хранение в таблице инфы по названиям 5 процедур селектов и 5 процедур lookup-ов в настоящий момент
позволяет мне разрабатывать РАЗНЫЕ отчеты не зависимо от exe и fr3. Кстати в критериях стали юзеры просить уже более
5 lookup, много справочников хотят, наверно увеличу до 10. Также перед запуском отчета я передаю список ID, что выбрал
в гриде юзер, т.к. много отчетов и зависимых от выбора юзера строк. Я работаю с devexpress, вот код получения списка ID,
при вызове отчета из меню главной формы:
function TCore.GetGridIDs(aMDIform: TForm): string;
var
i,j,k: integer;
g: TfrGrid;
vId: integer;
begin
Result := '';
for i := aMDIform.MDIChildCount - 1 downto 0 do
with aMDIform.MDIChildren do
if Active then
for j := 0 to ControlCount - 1 do
if (Controls[j].ClassType = TfrGrid) and (TfrGrid(Controls[j]).grdGrid.Focused) then
begin
g := TfrGrid(Controls[j]);
vId := g.GetColumnIdx('id');
if vId = -1 then exit;
with g.grdGrid.Controller do
for k := 0 to SelectedRecordCount - 1 do Result := Result + SelectedRows[k].DisplayTexts[vId] + ',';
Result := copy(Result,1,length(Result)-1);
exit;
end;
end;
Я знаю, что процедуры мало кто юзают, но тогда теряется вся прелесть гибкости.
Очень важно различать смысл Param и Field.
Param я определяю автоматом по названию процедур, а там еще и типы параметров IN, INOUT, OUT.
Field читаю только после заполнения Param.
Вот частичный пример отчета в БД состоящий из 1 процедуры селекта и 1 lookup.
Соответственно 1 селект юзаем в MasterData1, а lookup в DBlookup диалог окна критерия.
Как видно, что Param хранимой процедуры aSigner мой клиент делфи понимая по признаку OUT читает его из БД и
заливает потом в отчет. Сами данные отчета это возвращенный курсор хранимой процедуры, открытый в делфях по OraStoredProc.Open
function rep_1(
aBegin date,
aEnd date,
aBank number,
aSigner out varchar2
) return sys_refcursor
is
vCur sys_refcursor;
begin
begin
select name into aSigner from secur u where u.username = user;
open vCur for
select r.rolename, u.name, cnt_sign, cnt_nosign, cnt_forsign
from
(
select
doc_id,
sign_user,
...
where
t.sign_user = u.username and
t.doc_id = r.rfrl_id
order by 1, 2;
return vCur;
exception
when no_data_found then null;
end;
end;
function rep_1_Bank return sys_refcursor
is
vCur sys_refcursor;
begin
open vCur for select c.id, c.name from bank c order by name;
return vCur;
exception when no_data_found then null;
end;
Использование хранимых процедур для в моей идеи решает ряд важных задач:
- высокая безопасность, т.к. доступ к коду sql запроса исключен
- исправление логики извлечения данных отчетов можно полностью контролировать(исправлять) в БД
- унификация/систематизация отчетов(все процедуры отчета описаны в одной таблице). В автономных отчетах искать запрос и или процедуру будет весьма не удобно, как разработчику, так и службе безопасности при делегировании прав...
- благодаря стандартному универсальному подходу можно реализовать автоматический запуск разных отчетов по расписанию, пока не делал, не требуется
В MSSQL также можно легко применить мою идею.
gpi вы там выше написали, что ничего не поняли, могу пояснить по любому открытому вопросу еще детальнее.
Объясните "на пальцах" что же Вы сделали. Лично я ничего не понял. Что значит "независимость от exe"? Что это такое? Для чего нужно? А потом уже будем ругаться
У себя храню отчёте в базе, при старте читаются хранимкой все необходимые отчёты для данного приложения и инициализируются в соответствующие комбобоксы (пока трафик нет смысла экономить). При вызове - создаётся отчет (если в первый раз), запихивается в него шаблон и запускается. Параметры для отчета через переменные
Да ладно, никому не собирался грубить, на обиженных воду возят...
3 и 7 пост являются исчерпывающими для понимания, "что" было сделано.
Я так понимаю не прояснено более внятно "почему" это было сделано и как следствие много не пониманий о чем
речь. У меня за плечами 4 летний опыт работы в бизнес интеллидженс, др. словами клепал я отчеты сотнями...
И так, "почему" я решал проблему независимости.
Во-первых, чтобы писать программы, а не отчеты.
Во-вторых, на разработке отчетов люди(часто прогеры) тратят очень много времени, из них только 20% на логику
и 80% на оформление.
В третьих, у нас тут по штату есть кому отчеты писать, так почему прогер должен это делать?
Правила, которые соблюдает уважающий себя репортер:
1. Не мешать программистам. Не трогать их *.exe проекты. Иметь возможность самостоятельно создавать и
редактировать отчеты для прогеров. Задачи должны быть раздельными, прогеры пишут, а репортеры зная только
SQL писали отчеты.
2. Диалог окно отчета должно иметь в критерях lookup. Lookup - это комбобокс возвращающий данные из БД.
Т.к. масса отчетов требуют выбора записи из списка.
3. Никаких сетевых доступов, т.е. не надо чтобы пользователь из-за кривых ручек сисадминов имел гемор с
файлами лежащими на шарах, например при загрузке отчетов fr3 или dll
4. Отчет должен получать входящие параметры из текущего грида юзера ID или список ID из программы прогеров
*.exe. Это очень важно, т.к. находясь например в таблице документов, требуется распечатать именно тот на
котором стоит курсор.
5. Репортер должен иметь возможность залить произвольное кол-во "точечных" данных в отчет. Точечные это не
мастер детаил или гридовые данные, а конкретные переменные, типа Имя пользователя, какие-то специфичные
данные и т.д.
Структура и шаги выполнения отчета:
1. Диалог окно критериев
2. Заполненное диалог окно юзером
3. Запуск отчета
4. Заполнение отчета(результат), как табличными данными, так и точечными.
Пункты 1 и 4 самые сложные в реализации независимости от exe и fr3. В зависимых проектах эти пункты
разрабатываются в самом дельфи, неважно полностью или частично, что естественно приводит к постоянной
перекомпиляции exe, или созданию dll(но тут репортер должен знать делфи).
Как я решил проблему я уже описал.
Теперь все понятно? Пишите еслив че.
Как сделано у меня. Сами отчеты лежат в базе. Если пользователю надо, он через справлчник открывает нужный отчет. Или создает новый.
Выставляет в каких формах и для каких пользователей будет доступен отчет.
В программе открывается форма - загружаются имена доступных отчетов в меню.
Если пользователь вызывает отчет. Отчет загружается из БД ему передаются необходимые параметры.
В том числе и списки выбранных значений. Типа вашего (TCore.GetGridIDs(aMDIform: TForm): string; или массив данных. что доступно для этой формы
Отчет выводится. какая привязка к EXE ? Создавай сколько хочешь отчетов.
Если в самом отчете нужно в диалоге нужно LookUP поля выводить - Перейди на вкладку данные - создай запрос и выводи из него
Или добавь переменные из параметров.
Поэтому я создал эту ветку. Зацените кол-во просмотров и за какой промежуток времени это их собралось ).
Konst,
Благодарю за частичное вникание в мою идею.
Вот перечитал ваш пост, вы хоть представляете сколько вопросов он нагенерил? а вы "ничего не понял".
И так вам вопросы на вскидку:
1. Какая БД ?
2. В каком событии и как передаются "необходимые параметры" ?
3. Каким образом вы принимаете параметры из п.2 ?
4. Если вы говорите, что юзер может создавать отчеты, то в каком месте и как вызывается открытие LookUp ? Только без гипотетических слов "можно", а именно практический метод, просто можно многое, а надо то что работает.
5. Вы используете контролы датасеты fr3 или дельфовский(exe) через провайдер ?
6. Контрол сессии БД вы юзаете fr3 или дельфовский(exe) ? Если fr3, то как создаете сессию, нет ли дыры в безопасности?
Буду ждать ответа по пунктам.
2. В переменных
3. Простым
4. Заполняются при старте отчёта, перед формой диалога
5. контролы и датасеты fr3
6. Коннект к базе из программы
1. Оракл
2. Пишем класс TfrxParams - список объектов типа TfrxParam. Параметр содержит название, тип и значение. Если надо - добавляем еще какие-либо характеристики. Регистрируем класс в движке. Да вообще вариантов масса - хоть передавай в отчет всю свою форму вместе с гридом.
3. Вариантов несколько. Например, в отчете создаем объект типа TfrxParams. Добавляем в него нужные параметры. Через вызов из отчета некой функции, экзешник заполняет параметры нужными значениями.
4. Опять же несколько вариантов. Например, у меня для типовых контролов (например, просмотр групп товаров) сделаны свои классы, которые зарегистрированы в движке. Юзер просто бросает их на форму и указывает сессию. Для нетиповых задачь опять же есть класс типа TQuery. Из экзешника передается экземпляр открытой с БД сессии.
5. Свои. На базе DOA.
6. Сессия передается из экзешника. Жесткого контроля по безопасности нет, т.к. не требуется задачей. Но не вижу проблем закрутить гайки.