MIDAS. Практическое применение

select * from REP_INOUT(:FromDate, :ToDate) order by TO_NAMEПри этом необходимо учитывать, что данные из этой процедуры получаются совершенно не в

MIDAS. Практическое применение

Информация

Компьютеры, программирование

Другие материалы по предмету

Компьютеры, программирование

Сдать работу со 100% гаранией
p>

RecsOut: Integer;

ProvOptions: TGetRecordOptions;

begin

cdsInOut.Active := False;

try

with cdsInOut.FieldDefs do

begin

Clear;

// Первые две колонки - поставщик

with AddFieldDef do

begin

Name := 'SenderID';

DataType := ftInteger;

Required := True;

end;

with AddFieldDef do

begin

Name := 'SenderName';

DataType := ftString;

Size := 180;

end;

// Теперь набор полей - получатели

ibqInOut.First;

while not ibqInOut.EOF do

begin

ReceiverFieldName :=

FieldPrefix + ibqInOut.FieldByName('TO_ID').AsString;

if IndexOf(ReceiverFieldName) = -1 then

with AddFieldDef do

begin

Name := ReceiverFieldName;

DataType := ftCurrency;

end;

ibqInOut.Next;

end;

end;

// Второй проход - заполнение суммами

cdsInOut.IndexFieldNames := 'SenderID';

cdsInOut.CreateDataSet;

with cdsInOut do

begin

ibqInOut.First;

while not ibqInOut.EOF do

begin

if FindKey([ibqInOut.FieldByName('FROM_ID').AsInteger]) then

Edit

else

Insert;

ReceiverFieldName :=

FieldPrefix + ibqInOut.FieldByName('TO_ID').asString;

if State = dsInsert then

FieldByName('SenderID').AsInteger :=

ibqInOut.FieldByName('FROM_ID').AsInteger;

FieldByName('SenderName').AsString :=

ibqInOut.FieldByName('FROM_NAME').AsString;

with (FieldByName(ReceiverFieldName) as TFloatField) do

begin

asCurrency :=

ibqInOut.FieldByName('FULL_SUM').AsCurrency;

// пока свойства заголовка не установлены

if DisplayFormat = '' then

// установим их

begin

DisplayLabel :=

ibqInOut.FieldByName('TO_NAME').AsString;

DisplayWidth := 10;

Currency := False;

DisplayFormat := '# ##0.00';

end;

end;

Post;

ibqInOut.Next;

end;

// название первой колонки

with FieldByName('SenderName') do

begin

DisplayLabel := 'Поставщики';

DisplayWidth := 30;

end;

FieldByName('SenderID').Visible := false;

end;

// Пусть провайдер позаботится о формировании пакета.

ProvOptions := [grMetadata, grReset];

Result := dspInOut.GetRecords(-1,RecsOut,Byte(ProvOptions));

finally

cdsInOut.Active := False;

end;

end;Хотя эта функция выглядит длинной и сложной, делается очень немного: организуется два прохода по ibqInOut, который к этому времени должен содержать результат выполнения хранимой процедуры. Предварительно создается два обязательных поля - SenderID и SenderName (ID и наименование поставщика). Во время первого прохода у cdsInOut создается список колонок (в FieldDefs) с именами вида 'Receiver_NN'. Затем создается набор данных командой CreateDataSet и организуется второй проход, в котором ячейки заполняются значениями сумм. При этом производится поиск поставщика по SenderID (с использованием индекса), если такого поставщика еще нет добавляется запись. Затем ячейке таблицы (с соответствующим Receiver_ID) присваивается сумма, полученная из хранимой процедуры. Попутно устанавливаются визуальные свойства полей. После прохода по результату запросу выставляются визуальные свойства первых двух колонок. Наконец, функция dspInOut.GetRecords возвращает ClientDataSet (вместе со свойствами полей), содержащий готовыйй отчет. Провайдер dspInOut нужен только чтобы в пакет были включены визуальные свойства полей. Для этого используется флаг grMetadata, а данные получаются прямым вызовом метода GetRecords. После получения пакета клиентский набор данных можно благополучно закрыть, что, собственно, и делается.

Для передачи содержимого отчета на клиентскую часть в библиотеке типов создается один метод, объявленный как:

function InOutData(FromDate, ToDate: TDateTime): OleVariant; safecall;Этот метод принимает параметры отчета, и выдает весь отчет, запакованный в OleVariant:

function TrdmReport.InOutData(FromDate, ToDate: TDateTime): OleVariant;

begin

lock;

try

ibdReport.Connected := True;

ibtInOut.StartTransaction;

try

with ibqInOut do

begin

ParamByName('FromDate').asDateTime := FromDate;

ParamByName('ToDate').asDateTime := ToDate;

Active := True;

Result := CollectInOutData;

Active := False;

end;

ibtInOut.Commit;

finally

ibtInOut.Active := False;

end;

finally

unlock;

end;

end;Функция InOutData устанавливает параметры запроса и выполняет его, после чего вызывает функцию CollectInOutData, которая выполняет основную работу.

На этом этапе сервер приложений полностью закончен, и можно, запустив его один раз для регистрации в реестре как СОМ-сервера, приступать к созданию клиентской части.

Клиент

Задача клиентского приложения взаимодействовать с пользователем и отображать нужную ему информацию.

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

В прилагаемых исходных текстах имеется клиентское приложение, содержащее три модуля данных (TdataModule), dmCommon, dmDoc и dmReport. Каждый из них предназначен для соединения с соответствующим удаленным модулем данных.

Я не буду здесь останавливаться подробно на описаниях реализации клиентской части, но некоторые особенности необходимо рассмотреть.

Для использования сервера приложений его библиотека типов импортирована в клиентское приложение.

ПРИМЕЧАНИЕ

Дело в том, что для соединения клиентского приложения с сервером в данном случае используется TSocketConnection (scDoc). При обращении к интерфейсу удаленного модуля как к variant (через свойство AppServer) вызовы методов сервера в некоторых случаях вызывают сбой (Access violation). Поэтому все вызовы я произвожу через dispinterface, имя которого отличается от имени исходного интерфейса суффиксом Disp. Импорт библиотеки типов как раз и позволяет обращаться к этому интерфейсу.

Кроме того, при обращении к серверу с импортированной библиотекой типов все параметры процедур проверяются на этапе компиляции, и вызов GetDispIDsOfNames не производится, что ускоряет вызовы методов.

Для импорта надо выбрать пункты меню Project -> Import Type Library, и выбрать в списке DocServer library. Не забудьте, что сервер при этом должен быть зарегистрирован в реестре. После этого остается отключить опцию Generate Component Wrapper и нажать Create Unit, поскольку компонент в данном случае не нужен, достаточно только объявлений.Работа с поставщиками и получателями

Свойство DMCommon.ClientName обеспечивает обращение к методу сервера:

property ClientName[ID: integer]: string read GetClientName;

 

function TDMCommon.GetClientName(ID: integer): string;

var

AServer: IrdmCommonDisp;

begin

Result := '';

if ID = 0 then Exit;

AServer := IrdmCommonDisp(scCommon.GetServer);

Result := AServer.ClientName[ID];

AServer := nil;

end;Компонент scCommon: TSocketConnection после соединения с сервером приложений выдает в качестве результата метода GetServer ссылку на интерфейс удаленного модуля данных, остается просто преобразовать ее к нужному типу.

Получение нового идентификатора для поставщика и получателя производится в обработчике события OnNewRecord:

procedure TDMCommon.cdsClientNewRecord(DataSet: TDataSet);

var

AServer: IrdmCommonDisp;

begin

AServer := IrdmCommonDisp(scCommon.GetServer);

cdsClient.FieldByName('CLIENT_ID').AsInteger := AServer.NewClientID;

AServer := nil;

end;Работа с документами

Удаление документа происходит прямо из списка. Это делается в обработчике события компонента TAction. А вот редактирование и добавление нового документа производится в отдельном модуле DMDoc, привязанном к rdmDoc:

procedure TDMCommon.actDelDocExecute(Sender: TObject);

begin

with cdsDocList do

begin

Delete;

ApplyUpdates(0);

end;

end;

 

function TDMDoc.ProcessDoc(DocID: Integer; NewDoc: Boolean): boolean;

var

AServer: IrdmDocDisp;

begin

AServer := IrdmDocDisp(scDoc.GetServer); // scDoc: TSocketConnection

if NewDoc then

AServer.CreateNewDoc

else

AServer.DocID := DocID;

try

cdsTitle.Active := True;

cdsBody.Active := True;

RecalcDocSum;

Result := ShowEditForm;

cdsTitle.Active := false;

cdsBody.Active := false;

finally

AServer.DocID := 0; // Отмена регистрации документа

end;

end;Как уже говорилось, если DocID становится равным 0, сервер закрывает документ.

Сумма документа запрашивается с сервера:

procedure TDMDoc.RecalcDoc

Похожие работы

<< < 2 3 4 5 6 7 8 > >>