Выполнение процедур на OLE-сервере

Общая информация

1.1. Поскольку БД SH4 является закрытой, для доступа к её данным существует специальная библиотека sh4ole.dll

1.2. Sh4Ole.dll можно скачать ftp://ftp.ucs.ru/storehouse/sh4/sh4ole/

1.2.1. Для проверки, качайте вместе с тестовой программой TestOle.exe

1.2. Через библиотеку происходит работа с сервером SH4 аналогично его оригинальным клиентам.

1.2.1. Доступны для вызова стандартные функции и процедуры, которые описаны в файле ftp://ftp.ucs.ru/storehouse/sh4/sh4ole/Interface.txt

1.2.2. Существует также способ выполнения произвольных процедур, который описан ниже.

1.3. Алгоритм выполнения практически любой процедуры дает возможность получать данные, недоступные через стандартные процедуры OLE-сервера

Описание алгоритма

Общее

2.1.1. Рассмотрим работу алгоритма на примерах

Получение списка корреспондентов

На данный момент описание процедур отсутствует, но есть возможность узнать предназначение процедуры и ее параметры с помощью приложения Sdbman.exe (Sdb server manager, входит в стандартный дистрибутив StoreHouse v4).

Для входа в приложение требуется административный пароль (по умолчанию, Admin без пароля).

Получение информации о процедуре:

  • запустить приложение Sdbman.exe,  перейти в меню "Пользователи" ⇒ "Список пользователей"
  • выбрать любого пользователя и два раза кликнуть на нем - на закладке "Права" отобразится список всех доступных прав, в названии каждого права указана процедура, которая отвечает за это право

Например, если перейти в списке прав в "Словари" \ "Корреспонденты" \ "Список корреспондентов", увидим право "Чтение списка (proc CorrList)" - процедура CorrList возвращает список корреспондентов, которые относятся к определенной группе (при этом мы должны знать RID этой группы - их можно узнать с помощью другой процедуры CorrTree )

  • для получения подробной информации о процедуре перейти в "Прочее" ⇒ "Выполнить запрос", указать в поле "Процедура:" имя необходимой процедуры (регистр букв важен!) и нажать на панели задач кнопку с изображением буквы "i" (Параметры).
  • после выполнения на закладке "Список параметров" отображается информация в следующем виде (пример для процедуры CorrList):
IN PARAMETERS
tUint32,Key Tag: 101.1.0 --------- data set # 0
tUint8 Tag: 102.2.0
OUT PARAMETERS
Array { --------- data set # 1
tUint32,Key Tag: 102.1.0
tUint32,NotNull Tag: 101.1.1
tStr[15],NotNull Tag: 102.3.0
tStr[47],NotNull Tag: 102.4.0
tUint8,NotNull Tag: 102.2.0
tUint8,NotNull Tag: 102.5.0
}
END OF PARAMETER LIST 

 

где

  • IN PARAMETERS - блок входных параметров
  • OUT PARAMETERS - блок выходных параметров

 

Эту информацию мы будем использовать для вызова процедуры CorrTree в собственном приложении (с использование OLE)

В файле ftp://ftp.ucs.ru/storehouse/sh4/sh4ole/Interface.txt есть пример алгоритма выполнения произвольного запроса:

*********************************************************************************
Алгоритм выполнения практически любого запроса
*********************************************************************************
IndQuery := Sh.pr_CreateProc (ProcName); // ProcName - Имя процедуры
Заполняем входные датасеты
sh.pr_SetValByName ( IndQuery, IndexDS, FldName, Value ); // IndexDS - Индекс датасета
// (входные выходные - сквозная нумерация 0..n
// FldName тэг поля n.n.n (например 209.3.0)
// Value - присваиваемое значение
sh.pr_SetValByName ( IndQuery, IndexDS, FldName, Value );
sh.pr_SetValByName ( IndQuery, IndexDS, FldName, Value );
sh.pr_SetValByName ( IndQuery, IndexDS, FldName, Value );
sh.pr_Post( IndQuery, IndexDS ); 

Примечание: если у процедуры нет входных параметров, процедура SetValByName  не вызывается 

--------------------------------------------------------
перед выполнением процедуры устанавливаем состояние датасета

Чтение состояния датасета для ArrayX
function pr_GetRecordStatus (IndexDS : integer): integer;
возвращает состояние датасета
0 - Select
1 - Insert
2 - Update
3 - Delete
Запись состояния датасета для ArrayX
function pr_SetRecordStatus ( IndexDS, NewValue : integer ): integer
возвращает состояние датасета
----------------------------------------------------------
sh.pr_ExecuteProc(IndQuery);

получение результата проход по датасету IndexDS

while sh.pr_EOF( IndQuery,IndexDS )<>1 do
begin
X := sh.pr_ValByName(IndQuery,IndexDS,FldName) // получит значение поля FldName в датасете IndexDS
sh.pr_Next(IndQuery,IndexDS);
end;

sh.pr_CloseProc(IndQuery);
*********************************************************************************

У себя в приложении мы должны вызвать IndQuery := Sh.pr_CreateProc (CorrList);

При заполнении входных датасетов нужно учитывать информацию, полученную выше:
*********************************************************************************
IN PARAMETERS
tUint32,Key Tag: 101.1.0 --------- data set # 0
tUint8 Tag: 102.2.0
*********************************************************************************

Мы видим, что для процедуры CorrList входным является только один датасет (data set # 0), поэтому при заполнении мы должны в качестве параметра IndexDS передать 1, т.е. получаем:

IndQuery := Sh.pr_CreateProc (CorrList);

sh.pr_SetValByName (IndQuery, 1, FldName, Value );
 

Далее надо заполнить параметр FldName - это тег поля в виде n.n.n

В блоке входных параметров мы видим, что используется два тега - Tag: 101.1.0 и Tag: 102.2.0 . Первый тег обозначает RID группы (дерева), к которой относиться корреспондент, второй - тип корреспондента (третье значение обычно не значущее - используется в том случае, если есть два одинаковых по сути объекта, например "Ключ (Rid) корреспондента", но типы объекта различаются. 

Например, тег 102.1.2 обозначает "Ключ (Rid) корреспондента-поставщика", тогда как тег 102.1.3 обозначает "Ключ (Rid) корреспондента-получателя".

Более подробное описание всех тегов есть в файле Sh_Tags.h ftp://ftp.ucs.ru/storehouse/sh4/sh4ole/Sh_Tags.zip  и Sh_TagsB.h ftp://ftp.ucs.ru/storehouse/sh4/sh4ole/sh_tagsB.zip

Таким образом, для получения списка корреспондентов нам предварительно надо получить RID группы и тип корреспондента (т.е. сначала надо выполнить другие процедуры для получения этих данных)

В итоге наш код будет выглядеть приблизительно так:

IndQuery := Sh.pr_CreateProc (CorrList);

//Заполняем входные датасеты:
sh.pr_SetValByName ( IndQuery, 1, 101.1.0, 1); //запрос всех
корреспондентов, которые относятся к группе с RID=1

sh.pr_SetValByName ( IndQuery, 1, 102.2.0, 2); //запрос всех
корреспондентов, которые имеют тип = 2 (физ лицо) - более подробно см. в
файле Sh_Tags.h

sh.pr_SetValByName ( IndQuery, 1, 102.2.0, 3); //запрос всех
корреспондентов, которые имеют тип = 3 (спец. корр) - более подробно см. в
файле Sh_Tags.h

//выполняем процедуру:
sh.pr_ExecuteProc(IndQuery);

//получение результата проход по датасету 1:
while sh.pr_EOF( IndQuery,1 )<>1 do
begin
X := sh.pr_ValByName(IndQuery,1,FldName) // получить значение поля
FldName в датасете 1 - вот здесь уже нужно подставить в качестве FldName
теги, которые относяться непосредственно к корреспонденту. Например
102.1.0 - RID корреспондента, 102.4.0 - наименование корреспондента и
т.д. (более подробно см. Sh_Tags.h)
sh.pr_Next(IndQuery,IndexDS);
end;

sh.pr_CloseProc(IndQuery);

В итоге мы получим информацию о всех искомых корреспондентах.

Следует помнить, перед вызовом процедуры CorrList нужно получить значения RID-ов всех групп корреспондентов (дерева)

И еще - все вышеописанное больше для примера, т.к. получить список корреспондентов (всех) можно и проще - с помощью процедуры CorrFullList

2.3. создание документа расхода

IndQuery :=  sh.pr_CreateProc( 'InsExpDoc' ) ;
// -----------------------------------------------------------------------------------------
// ЗАГОЛОВОК
// Заголовок неактивный, чтобы не заполнять группу станций
// для простоты без модификаторов
sh.pr_SetValByName ( IndQuery, 0, '230.2.0','AAA');    // Номер префикс tStr[19]
sh.pr_SetValByName ( IndQuery, 0, '230.3.0',1);        // Номер число   tUint32
// Для автоматического получения номера документа можно использовать процедуру sh: CalcExpDocNum

sh.pr_SetValByName ( IndQuery, 0, '230.4.0',Date());   // Дата док tShortDate,NotNull
sh.pr_SetValByName ( IndQuery, 0, '230.5.0',4 or 8 );  // Опции документа tUint32,NotNull

     {
      //  опции документов по расходу   ( expense document options )
        #define edoActive            2   // расход активный  (нужно заполнять группу станций)
        #define edoSaleTaxIncluded   4   // в суммы включен налог с продаж
        #define edoSaleTaxPayed      8   // налог с продаж оплачен
        #define edoImported         16   // документ сымпортирован - недопустима модификация
        #define edoLocked           32   // документ блокирован создавшим его пользователем
        // второй байт занят под степень скомплектованности/списанности док-та (младшие 4 бита: флаги ed_...)
       }

sh.pr_SetValByName ( IndQuery, 0, '214.1.0',NULL); // Группа станций tUint32,Key
        // Список мест реализации - Процедура SUnits;

sh.pr_SetValByName ( IndQuery, 0, '216.1.0',0); // Категория расхода tUint32,Key,NotNull
        // Список категорий расхода ExpCtgs

sh.pr_SetValByName ( IndQuery, 0, '230.7.0','Здесь комментарии'); // tStr[255]

sh.pr_Post( IndQuery,0 );
//
// Содержимое

// запись 1 - товар с RID = 2366 , комплект 67 , количество 10, ед.изм. 6
sh.pr_SetValByName ( IndQuery, 1,'231.1.0', 0);      //  Rid tUint32,Key
sh.pr_SetValByName ( IndQuery, 1,'231.2.0', 10 );    //  Количество
sh.pr_SetValByName ( IndQuery, 1,'231.3.0', 200);    //  Сумма tCurrency,NotNull
sh.pr_SetValByName ( IndQuery, 1,'212.2.10',0);      //  НДС tCurrency,NotNull
sh.pr_SetValByName ( IndQuery, 1,'213.2.10',0);      //  НСП tCurrency,NotNull
sh.pr_SetValByName ( IndQuery, 1,'206.1.0', 6);      //  Ед.изм.

sh.pr_SetValByName ( IndQuery, 1,'210.1.0', 2366);   // Товар
sh.pr_SetValByName ( IndQuery, 1,'200.1.0', 67);     //  Комплект для товара можно узнать из процедуры GoodsBase

sh.pr_SetValByName ( IndQuery, 1,'102.1.5', NULL);   // Склад списания, подобрать склад списания можно с помощью проц.sh: GoodsLink4Exp
sh.pr_SetValByName ( IndQuery, 1,'0.50.99',0);       //  НЕ трогать
sh.pr_SetValByName ( IndQuery, 1,'231.4.0',0);       //  Опции спецификаций
{
//  опции спецификаций документов по расходу   ( expense specification options )
#define esoService           1   // спецификация - услуга
#define esoHasMdf            2   // есть связанные со спецификацией модификаторы
#define esoTax1Rate          4   // использовать ставку для расчета налога 1 (НДС)
#define esoTax2Rate          8   // использовать ставку для расчета налога 2 (НСП)
#define esoRef              16   // спецификация - ссылка
}
sh.pr_Post( IndQuery,1 );

// запись 2 то же самое только количество 20, сумма 300
sh.pr_SetValByName ( IndQuery, 1,'231.1.0', 0);
sh.pr_SetValByName ( IndQuery, 1,'231.2.0', 20 );
sh.pr_SetValByName ( IndQuery, 1,'231.3.0', 300);
sh.pr_SetValByName ( IndQuery, 1,'212.2.10',0);
sh.pr_SetValByName ( IndQuery, 1,'213.2.10',0);
sh.pr_SetValByName ( IndQuery, 1,'206.1.0', 6);
sh.pr_SetValByName ( IndQuery, 1,'210.1.0', 2366);
sh.pr_SetValByName ( IndQuery, 1,'200.1.0', 67);
sh.pr_SetValByName ( IndQuery, 1,'102.1.5', NULL);
sh.pr_SetValByName ( IndQuery, 1,'0.50.99',0);
sh.pr_SetValByName ( IndQuery, 1,'231.4.0',0);
sh.pr_Post( IndQuery,1 );

SH.pr_SetRecordStatus ( 1,1 );
sh.pr_ExecuteProc(IndQuery);
SH.pr_SetRecordStatus ( 1,0 );

ShowMessage(sh.GetExcMessage);
sh.pr_CloseProc(IndQuery);

end;

end.