Выполнение процедур на OLE-сервере
Общая информация
Поскольку БД SH4 является закрытой, для доступа к её данным существует специальная библиотека sh4ole.dll
Sh4Ole.dll можно скачать ftp://ftp.ucs.ru/storehouse/sh4/sh4ole/
Для проверки, качайте вместе с тестовой программой TestOle.exe
Через библиотеку происходит работа с сервером SH4 аналогично его оригинальным клиентам.
Доступны для вызова стандартные функции и процедуры, которые описаны в файле ftp://ftp.ucs.ru/storehouse/sh4/sh4ole/Interface.txt
Существует также способ выполнения произвольных процедур, который описан ниже.
Алгоритм выполнения практически любой процедуры дает возможность получать данные, недоступные через стандартные процедуры OLE-сервера
Описание алгоритма
Общее
Рассмотрим работу алгоритма на примерах
Получение списка корреспондентов
На данный момент описание процедур отсутствует, но есть возможность узнать предназначение процедуры и ее параметры с помощью приложения 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
Создание документа расхода
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.