Delphi World - это проект, являющийся сборником статей и малодокументированных возможностей  по программированию в среде Delphi. Здесь вы найдёте работы по следующим категориям: delphi, delfi, borland, bds, дельфи, делфи, дэльфи, дэлфи, programming, example, программирование, исходные коды, code, исходники, source, sources, сорцы, сорсы, soft, programs, программы, and, how, delphiworld, базы данных, графика, игры, интернет, сети, компоненты, классы, мультимедиа, ос, железо, программа, интерфейс, рабочий стол, синтаксис, технологии, файловая система...
Delphi и 1C - экспорт и импорт

Автор: Александр Авдошин
Специально для Королевства Delphi

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

  • С помощью метода EvalExpr(<СтрокаВыражения>)
    Метод EvalExpr вычисляет выражение, записанное параметре <СтрокаВыражения> на встроенном языке 1С:Предприятие и возвращает результат вычисления. Результатом выражения может быть число, строка, дата или значение любого агрегатного типа данных.
  • С помощью метода CreateObject(<ИмяАгрегатногоТипа>)
    Метод CreateObject создает объект агрегатного типа данных системы 1С:Предприятие и возвращает ссылку на него. Данная функция обычно используется одновременно с явным определением переменной типа OLEVariant и присвоением ей ссылки на объект агрегатного типа данных.
  • С помощью метода ExecuteBatch(<СтрокаОператоров>)
    Метод ExecuteBatch выполняет последовательность операторов, записанную в параметре <СтрокаОператоров> на встроенном языке 1С:Предприятие. Метод возвращает -1, если последовательность операторов выполнена успешно, или 0 в противном случае.
  • Вызовом атрибутов и методов системы 1С:Предприятие как OLE Automation сервера

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

  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С:Предприятие".

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

Проект Delphi World © Выпуск 2002 - 2004
Автор проекта: ___Nikolay