");
// -->
Довольно часто перед программистами, работающими в небольших компаниях, стоит
проблема импорта данных из программы "1С:Предприятие", или экспорта в нее же.
Причин тому может быть множество - например, желание автоматизировать обновление
прайс-листа на веб-страничке компании на основании реальных данных, или же
автоматизация ввода первичных документов, отправляемых по электронной почте
компанией-поставщиком. Какая бы задача подобного рода ни стояла перед
программистом, она, как правило, успешно решается с помощью связки Delphi-1C. В
этой статье я хотел бы дать рекомендации и разъяснить некоторые аспекты
использования механизма OLE Automation применительно к программе "1С:Предприятие
версия 7.7".
Перед прочтением статьи я настоятельно рекомендую Вам ознакомиться с книгой
"Delphi 4 Unleashed" Чарльза Калверта и с главой "Связь с внешними приложениями
посредством механизмов DDE и OLE Automation" книги "1С:Предприятие 7.7 Описание
встроенного языка". Также я предполагаю, что вы имеете опыт программирования как
в среде Delphi, так и в среде "1С:Предприятие".
Первые шаги
Ну, во-первых, прежде чем использовать все возможности программы
"1С:Предприятие", необходимо сначала создать соответствующий OLE-объект.
Идентификатор этого OLE-объекта зависит от версии и типа установленной программы
"1С:Предприятие":
- V1CEnterprise.Application - версия независимый ключ
- V77.Application - версия зависимый ключ
- V77S.Application - версия зависимый ключ, SQL-версия
- V77L.Application - версия зависимый ключ, локальная версия
- V77M.Application - версия зависимый ключ, сетевая версия
Например, создадим OLE-объект для сервера "1С:Предприятие". Для простоты
создадим объект без привязки к конкретной версии и типу программы:
procedure TForm1.Create1C;
var
onesobj: Olevariant;
begin
onesobj := createoleobject('V1CEnterprise.Application');
end;
Затем мы должны проинициализировать систему методом Initialize, имеющим
следующие параметры:
Initialize(<Имя_Объекта>.RMTrade,<КоманднаяСтрока>,<ПустаяСтрока>), где:
<Имя_Объекта> - Идентификатор созданного OLE объекта
<КоманднаяСтрока> - Строковое выражение - командная строка запуска
<ПустаяСтрока> - Строковое выражение. Может содержать пустую строку или
строковое значение "NO_SPLASH_SHOW" - отключить заставку при запуске системы.
Метод Initialize возвратит значение логического типа: TRUE, если
инициализация прошла удачно, или FALSE в противном случае. Следует иметь в виду,
что в OLE Automation TRUE и FALSE имеют соответственно значения -1 (минус
единица) и 0.
Параметры командной строки запуска подробно описаны в руководстве к программе
"1С:Предприятие", здесь же я приведу лишь те, которые могут оказаться вам
полезными:
/DПуть к базе - задает путь к базе программы.
/M - запуск программы в монопольном режиме
/NИмя пользователя
/PПароль - пароль указанного пользователя
Параметры, не указанные в командной строке, будут запрошены программой в
диалоговом режиме.
Например, инициализация программы в монопольном режиме с явным указанием пути
к базе данных (D:uh2001test), имени пользователя (Саша) и пароля (12345) без
вывода на экран заставки выполняется следующим образом (здесь и далее
подразумевается, что объект onesobj уже создан оператором createoleobject):
onesobj.initialize(onesobj.rmtrade,'/DD:uh2001test /M /NСаша /P12345','NO_SPLASH_SHOW');
В отличие от, например, OLE Automation-сервера приложения Microsoft Excel,
сервер программы "1С-Предприятие" запускается в режиме "hide", то есть рабочее
окно программы не отображается на экране.
Для использования созданного и проинициализированного объекта необходимо
просто обращаться к атрибутам и методам системы 1С:Предприятие как OLE
Automation сервера.
Для завершения работы с программой необходимо освободить OLE-объект путем
присвоения ему значения UnAssigned:
onesobj := UnAssigned;
Впрочем, это необязательно, так как при закрытии вашего приложения OLE-объект
будет освобожден автоматически.
Просуммируем полученные знания: создадим OLE-объект "1С:Предприятие",
проинициализируем его и корректно освободим:
procedure TForm1.Create1C;
var
onesobj: Olevariant;
begin
onesobj := createoleobject('V1CEnterprise.Application');
onesobj.initialize(onesobj.rmtrade,
'/DD:uh2001test /M /NСаша /P12345', 'NO_SPLASH_SHOW');
onesobj := UnAssigned;
end;
Как работать с полученным объектом
Резонный вопрос. Собственно, ради этого все и затевалось, не так ли? :) На
самом деле, все очень просто. После того, как мы создали и проинициализировали
OLE-объект, работать с ним можно следующим образом:
- С помощью метода EvalExpr(<СтрокаВыражения>)
Метод EvalExpr вычисляет выражение, записанное параметре <СтрокаВыражения> на
встроенном языке 1С:Предприятие и возвращает результат вычисления. Результатом
выражения может быть число, строка, дата или значение любого агрегатного типа
данных.
- С помощью метода CreateObject(<ИмяАгрегатногоТипа>)
Метод CreateObject создает объект агрегатного типа данных системы
1С:Предприятие и возвращает ссылку на него. Данная функция обычно используется
одновременно с явным определением переменной типа OLEVariant и присвоением ей
ссылки на объект агрегатного типа данных.
- С помощью метода ExecuteBatch(<СтрокаОператоров>)
Метод ExecuteBatch выполняет последовательность операторов, записанную в
параметре <СтрокаОператоров> на встроенном языке 1С:Предприятие. Метод
возвращает -1, если последовательность операторов выполнена успешно, или 0 в
противном случае.
- Вызовом атрибутов и методов системы 1С:Предприятие как OLE Automation
сервера
Здесь есть несколько подводных камней, о которых я сразу хочу предупредить:
- При вызове атрибутов и методов системы 1С:Предприятие необходимо
использовать их англоязычные синонимы (они указаны для каждого метода в книге
"Описание встроенного языка")
- Для создаваемого агрегатного типа данных в среде Delphi необходимо завести
переменную типа OLEVariant
- В случае, если вызываемый метод OLE-объекта не требует параметров (либо
один из параметров является необязательным), в качестве параметра ему
необходимо передавать EmptyParam (либо - для Delphi 3 - пустую строку).
- Для обращения к русскоязычным идентификаторам объектов агрегатных типов (например,
реквизитов справочников) следует использовать метод объекта агрегатного типа
getattrib(<ИмяАтрибута>) для получения значения атрибута, и
setattrib(<ИмяАтрибута>) для установки значения.
Для комплексной иллюстрации всего вышеописанного я приведу пример, в котором
содержимое справочника "Номенклатура" целиком экспортируется в таблицу базы
данных (в примере подразумевается, что уже создана таблица table1, поля которой
адекватны справочнику. Таблица table2 ссылается на ту же физическую таблицу, что
и table1, и служит лишь для поиска уже добавленных элементов):
procedure TForm1.exportsprav;
var
counter: integer; //Счетчик импортированных записей
onesobj: Olevariant; //OLE-объект программы 1С:Предприятие
ware, ware2: olevariant; //Агрегатные объекты
val, edizm, nds, np: olevariant;
pf: integer; //Промежуточные переменные
begin
table1.open; //Открываем таблицу1
table2.open; //Открываем таблицу2
counter := 0; //Обнуляем счетчик записей
onesobj := createoleobject('V1CEnterprise.Application'); //Создаем OLE-объект
//Инициализируем объект
onesobj.initialize(onesobj.rmtrade, '/DD:uh2001test /M /NСаша /P12345', 'NO_SPLASH_SHOW');
//Создаем необходимые агрегатные объекты
ware := onesobj.createobject('Справочник.Номенклатура');
ware2 := onesobj.createobject('Справочник.Номенклатура');
edizm := onesobj.createobject('Справочник.ЕдиницыИзмерений');
nds := onesobj.createobject('Справочник.СтавкиНДС');
np := onesobj.createobject('Справочник.СтавкиНП');
ware.selectgroup(1); //Устанавливаем режим выборки групп
ware.selectitems(1); //Открываем выборку элементов справочника
while ware.GetItem(1) > 0 do //Выбираем все элементы
begin
if ware.level('') = 1 then //Если мы выбрали группу первого уровня, то
pf := -1
else
begin
//Иначе ищем элемент-родитель
ware2.FindItem(ware.getattrib('Родитель'));
if table2.findkey([ware2.getattrib('Код')]) then
//Если этот элемент мы уже импортировали
pf := table2.fieldbyname('ID').AsInteger //, то получаем его код
else
pf := -1; //иначе помещаем элемент в группу первого уровня
end;
if ware.deletemark('') = 0 then //Если элемент не удален, то
begin
table1.append; //добавляем новое поле к таблице
//Заполняем поля таблицы значениями соответствующих атрибутов элемента справочника
table1.fieldbyname('CODE_1S').AsInteger := ware.getAttrib('Код');
//Заполняем поле наименования
table1.fieldbyname('NAME').AsString := ware.getAttrib('Наименование');
table1.fieldbyname('PARENT_FOLDER').AsInteger := pf;
table1.fieldbyname('FULLNAME').AsString := ware.getAttrib('ПолнНаименование');
//Ищем соответствующую запись в справочнике "единицы измерения"
edizm.finditem(ware.getattrib('ЕдиницаИзмерения'));
//Заполняем поле единицы измерения
table1.fieldbyname('EDIZM').AsString := edizm.getattrib('Наименование');
//так мы получаем значения периодических реквизитов
table1.fieldbyname('SEBESTOIM').AsFloat :=
ware.getAttrib('Себестоимость').GetValue(datetostr(now));
table1.fieldbyname('PRICEOPT').AsFloat := ware.getAttrib('Цена');
nds.finditem(ware.getAttrib('СтавкаНДС').GetValue(datetostr(now)));
np.finditem(ware.getAttrib('СтавкаНП').GetValue(datetostr(now)));
//Заполняем поле ставки НДС
table1.fieldbyname('STNDS').AsFloat := nds.getAttrib('Ставка');
//Заполняем поле ставки НП
table1.fieldbyname('STNP').AsFloat := np.getAttrib('Ставка');
table1.fieldbyname('ARTICUL').AsString := ware.getAttrib('Артикул');
if Ware.IsGroup('') = 1 then //Если мы выбрали группу товара, то
table1.fieldbyname('IS_FOLDER').AsInteger := 1
else
table1.fieldbyname('IS_FOLDER').AsInteger := 0;
table1.post;
table2.refresh;
end;
inc(counter);
end;
end;
Заключение
К сожалению, невозможно вместить в одну статью всю информацию, которая была
бы вам полезна. Я постарался дать лишь тот минимум, который необходим для
получения некоторых базовых знаний, и способен стать фундаментом для ваших
собственных маленьких открытий в области интеграции Delphi и "1С:Предприятие".
Пишите мне,
задавайте вопросы, и вполне возможно, что вскоре появится продолжение статьи.
|