Delphi и 1C - экспорт и импорт

Материал из DRKB


Специально для Королевства 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С:Предприятие необходимо использовать их англоязычные синонимы (они указаны для каждого метода в книге "Описание встроенного языка")
  • Для создаваемого агрегатного типа данных в среде 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:\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С:Предприятие".

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


Author: Александр Авдошин
Source: http://delphiworld.narod.ru
ID: 04486