Журнал Progman - журнал об программировании Автор Александр Авдошин,

Довольно часто перед программистами, работающими в небольших компаниях, стоит проблема импорта данных из программы "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:\buh2001test), имени пользователя (Саша) и пароля (12345) без
вывода на экран заставки выполняется следующим образом (здесь и далее подразумевается,
что объект onesobj уже создан оператором createoleobject):

onesobj.initialize(onesobj.rmtrade,'/DD:\buh2001test /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:\buh2001test /M /NСаша /P12345','NO_SPLASH_SHOW'); 
      onesobj := UnAssigned;
  end; 

. После того, как мы создали и проинициализировали OLE-объект, работать с ним можно следующим образом:

Здесь есть несколько подводных камней, о которых я сразу
хочу предупредить:

  1. При вызове атрибутов и методов системы 1С:Предприятие
    необходимо использовать их англоязычные синонимы (они указаны для каждого метода
    в книге "Описание встроенного языка")
  2. Для создаваемого агрегатного типа данных в среде Delphi
    необходимо завести переменную типа OLEVariant
  3. В случае, если вызываемый метод OLE-объекта не требует
    параметров (либо один из параметров является необязательным), в качестве
    параметра ему необходимо передавать EmptyParam (либо - для Delphi 3 - пустую
    строку).
  4. Для обращения к русскоязычным идентификаторам объектов
    агрегатных типов (например, реквизитов справочников) следует использовать
    метод объекта агрегатного типа 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:\buh2001test /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С:Предприятие"
.

Пишите мне, задавайте вопросы,
и вполне возможно, что вскоре появится продолжение статьи.